Building the NestJS backend
In this first part of the series, we will focus on building the backend API with Nest.js. The second part is about building the user interface and handling all frontend logic using React.
- You can find the second part here. The complete source code developed throughout this series can be found in this GitHub repository if you will prefer to head straight into the code.
- Basic knowledge and previous experience with building web applications with React will help you to get the best out of this series.
- Although not mandatory, you should know a few things about TypeScript.
And to make it easy for everyone to follow along, I will endeavor to break down any complex implementation. Furthermore, you will also need to ensure that you have Node.js and Yarn package manager installed in your development machine. If you are yet to get them installed, kindly follow the instructions here to learn how to install Node.js properly and here for Yarn package manager.
Also, you need to have MongoDB installed on your machine.
Follow the instructions here to download and install it for your choice of the operating system.
To successfully install MongoDB, you can either install it by using Homebrew on Mac or by downloading it from the MongoDB website. This tutorial uses a macOS machine for development. If you’re using another operating system, you may need to use sudo for npm commands in the first steps.
Nest.js is a progressive Node.js framework with a modular architecture for building efficient, reliable, and scalable server-side applications.
Next.js vs. React: How do they compare?
It was designed and developed to help improve the productivity of developers when building large and complex programs by adding extra features that ensure the successful development of awesome applications with fewer bugs.
You can easily focus on implementing new features without getting too worried over breaking an existing application in production, as TypeScript can allow you to easily spot errors in the code at a very early stage.
What is Next.js?
As pointed out earlier, in this tutorial, we will combine these awesome contemporary web tools and build an application with it. At the end of the article, you would have gathered enough knowledge for you to explore the benefits of building applications by combining React and TypeScript in either a new project or to enhance your existing projects. As you may be aware, React is a component-based frontend framework and make use of props and state objects here and there.
Building React applications with TypeScript allows you to have strongly-typed components that have well defined and identifiable props and state objects. This will ensure that the usages of your components are type-checked by the compiler, and you can spot errors on time.
- This will, in a nutshell, make you more productive as a developer and make your code easier to read and understand.
- "React is a component-based frontend framework."
- You are going to build a blog application with which users can:.
Create and save a new post. View the newly saved post and all other created posts on the homepage. Carry out processes such as editing and deleting posts. Also, to persist data into the database, you will make use of MongoDB. Here is a preview of what to expect at the end of this tutorial:. This application will allow users to create a blog post only if they have been authenticated and authorized.
Building Backend APIs with Nest.js
Otherwise, they will only be able to view the created posts by authenticated users. Authentication and authorization of users will be handled by Auth0. You will start gradually and take things one step at a time, starting by building the complete backend API and allow any user to have access and make successful API calls to create, retrieve, and edit data in the database. Then you will proceed to secure the API by managing user authentication via Auth0. To test all the implementation in this part of the series, you will use Postman.
- As mentioned, the backend API will be built using Nest.js. Here, you will start by installing and configuring Nest.js and then proceed to flesh out the structure necessary for the API.
- You can easily install a new Nest.js project using the command-line interface built specifically for scaffolding Nest applications called Nest CLI.
- You will start by using the command-line interface built specifically for scaffolding a new Nest.js application to create yours.
Alternatively, you can clone the starter project for Nest.js here on GitHub. Even though both approaches will produce the same outcome, for the sake of this tutorial and as it recommended for a first time user by the Nest.js team, you will use the Nest CLI to create your project.
Install the CLI by running the following command:. Once the process is complete, confirm if Nest CLI has been installed by running the following command:. You will see an output similar to the following indicating the version installed on your machine:.
Create your React Application
💡 Please note that this version might be different from yours. Craft a new project for this tutorial by using the nest command as shown here:.
- Immediately after running the preceding command, nest will prompt you to choose a package manager you would like to use.
- Select npm and hit ENTER on your computer to start installing Nest.js:.
- This will create a new Nest.js project in a blog-backend directory within your local development folder.
- Now, move into the newly created directory and run a command to install other required server dependencies:.
Because Nest.js supports integrating with MongoDB database by using Mongoose, what you have done here is to install mongoose and the dedicated package created by Nest.js team for the integration named @nestjs/mongoose. Once the installation process is complete, you can then easily import the MongooseModule into your application.
More about this later in the tutorial. Next, before you start the application, open the project using your code editor, and edit the default port as shown below:. What you have done here is to change the default port of Nest.js to 5000 to avoid port conflict with the React application, which you will build in the next part of this series. That will run on port 3000 by default.
With that done, start the application using the following command:. This will run the application on port 5000 on your local machine. Navigate to http://localhost:5000 from your browser of choice, and you will see your application running. In this section, you have successfully installed the Nest CLI and leveraged it to generate the project for this tutorial.
You then proceeded to run the application on the default port 5000. Next, you will work on ensuring a successful connection between your app and its database that will be created. In this section, you will start by setting up the configuration necessary for database connection, which includes configuring and integrating MongoDB into your application.
As instructed in the prerequisites section of this tutorial, you should have MongoDB installed by now. If this is your first time installing it, you should have it running automatically at the moment and might not be necessary to start it. To check if MongoDB is currently running, open a different terminal window to keep the backend application running and run the following command :.
If an output similar to the one below is displayed in your terminal, then MongoDB is running:. Otherwise, execute the following command to start MongoDB:. Note: MongoDB usually stores data in /data/db within the root directory of your computer’s operating system, but If your operating system is macOS Catalina or ran into an error while running the command above, the root folder is not writable.
To handle this, you will have to create a different directory in another location and reference that path when running the command, as shown here: sudo mongod --dbpath=/Users/
Create your Okta Application
Next, open this file ./src/app.module.ts and update its content, as shown here:. Here, you imported the MongooseModule into the root AppModule and then used the forRoot() method to supply the connection to the database.
- Once the editing of the above file is completed, you have now successfully set up a database connection for your application by using the Mongoose module for MongoDB.
- Here, you will define the structure and datatype of the data in your application by creating a TypeScript interface, which will be used for type-checking and to define the types of data that should be passed into the application.
Also, you will create a database schema, as Mongoose tends to derive everything from a Schema defined within a particular application. To begin, go back to the terminal where the application is currently running and stop the process with CTRL+C, then navigate back into the ./src/ directory and create a directory named blog within it.
Now create a sub-directory named schemas within the initially created blog directory. This new directory will house all the database schema that will be required by your application. Now, create a schema file and called it blog.schema.ts and save it within the schemas folder. Open this file and add the following content within it:.
This definition specifies that all fields will store and only accept string values. With this in place, the datatype of data that will be stored in the database will be properly controlled.
Next, you will create the interface majorly for type-checking. To begin, create a new directory named interfaces within the /blog-backend/src/blog folder. And within it, create a file and name it post.interface.ts and add the following code to it:. Here you have successfully defined the types of data for a Post type as string values. A data transfer object will help define how data will be sent over the network and control how data will be posted from the application to the database.
To achieve this, create a directory dto inside the ./src/blog folder. Within the newly created folder, create a new file and name it create-post.dto.ts.
Paste the following code into it:. From the preceding code snippet, you have marked each of the individual properties in the CreatePostDTO class to have a data type of string and as read-only to avoid unnecessary mutation. Module in Nest.js is a class annotated with a @Module() decorator. It helps to keep the application structure organized. It is recommended by Nest.js that each application should have at least one module, mostly the root module. In view of that, you will use the nest command to generate a module for your application.
To do that, ensure that you are still within the blog-backend directory and execute the following command:. The command above will generate a new module named blog.module.ts for the application and update the root module for it by automatically importing the newly created BlogModule.
With this in place, Nest.js will be aware of another module within the application besides the root module. The generated blog module file will look like this:. You will update this BlogModule with the required contents later in the tutorial. Here you will generate a service, also known as a provider, and afterward, you will create a controller to handle all HTTP requests from the application. Services in Nest.js are meant only to handle any complex business logic for a specific purpose and return the appropriate response to the controller.
Run the following command while you are still within the project directory to generate a new service file:.
The thing to note here is that the nest command above will create a blog.service.spec.ts file, which you can use for testing. It has also created a new blog.service.ts file, which will hold all the logic for this application and then communicate with the MongoDB database by adding and retrieving data from it. Lastly, it has also automatically imported the newly created service and added it to the blog.module.ts.
Open the newly created blog.service.ts and replace its default contents with the following code with methods for creating a post, retrieving all created posts, and fetching the details of a single post from the database:.
In this file, you first imported the required module from @nestjs/common, mongoose, and @nestjs/mongoose. You also imported an interface named Post and a data transfer object CreatePostDTO. In the constructor, you added @InjectModel('Post'), which will inject the Post model into this BlogService class.
With that, you will now be able to use this injected model to retrieve all posts, fetch a single post, and carry out other database-related activities.
Next, you created addPost(), getPost() and getPosts() methods to add a new post, retrieve a single post and fetch all posts from the database respectively. Lastly, within this file, to enable editing and deleting any created post, you need to add the following immediately after the getPosts() method, as shown here:.
All the methods created above will help facilitate proper interaction with the MongoDB database from the backend API.
You can now proceed to create the required routes that will handle HTTP calls from a front-end client. Controllers in Nest.js are meant to receive incoming HTTP requests from an application frontend and return an appropriate response. This will ensure that the controller is not bloated as most of the business logic has been abstracted to a service. You have created the service earlier, so here, you will leverage the nest command to generate a new controller file by running the following command, while you are still within the blog-backend project directory:.
Full visibility into production React apps
The preceding command created two new files within the src/blog directory, blog.controller.spec.ts, and blog.controller.ts. You can ignore the former file for now, as you won’t be writing any tests in this tutorial. The latter file is the controller itself and it is a TypeScript file decorated with @Controller metadata, as it is obtainable for every controller created in Nest.js.
Now, open the blog.controller.ts file with your text editor and update its content with the following:. From the code snippet above, you imported all the necessary modules to handle HTTP requests from @nestjs/common module. You then proceeded to import three new modules, which are: BlogService, CreatePostDTO, and ValidateObjectId. You will create the ValidateObjectId module in the next section.
To have access to all the functions declared within the BlogService earlier, you injected it into the controller via a constructor.
This is a pattern regarded as dependency injection used in Nest.js to increase efficiency and enhance the modularity of the application. Finally, you created the following asynchronous methods:. getPosts(): This method will carry out the functionality of receiving an HTTP GET request from the client to fetch all posts from the database and then return the appropriate response. It is decorated with a @Get('posts'). getPost(): This takes a postID as a parameter and fetches a single post from the database.
What You Will Build
In addition to the postID parameter passed to this method, you realized the addition of an extra method named ValidateObjectId(). This method implements the PipeTransform interface from Nest.js. Its purpose is to validate and ensure that the postID parameter can be found in the database.
You will define this method in the next section. addPost(): This method will handle a POST HTTP request to add a new post to the database. For editing and deleting a particular post, add two more methods to the blog.controller.ts file.
React vs. Next.js: Library vs Framework
To accomplish that, paste the editPost() and deletePost() methods directly after the addPost() method that you previously added in the same file:. Here you have added:. editPost(): This method accepts a query parameter of postID and will carry out the functionality of updating a single post. It also made use of the ValidateObjectId method to provide proper validation for the post that you need to edit.
deletePost(): This method will accept a query parameter of postID and will delete a particular post from the database. Similarly to the BlogController, each of the asynchronous methods you have defined here has a metadata decorator and takes in a prefix that Nest.js uses as a routing mechanism.
It controls which controller receives which requests and points to the methods that should process the request and return a response, respectively.
For example, the BlogController that you have created in this section has a prefix of blog and a method named getPosts() that takes in a prefix of posts. This means that any GET request sent to an endpoint of blog/posts (http:localhost:3000/blog/posts) will be handled by the getPosts()method.
This example is similar to how other methods will handle HTTP requests. Navigate to the blog directory and create a new folder named shared. Now create another folder within the newly created folder and name it pipes. Then finally, create a new file within the newly created folder and name it validate-object-id.pipes.ts.
Open this file and add the following content to define the accepted postID data:. The ValidateObjectId() class implements the PipeTransform method from the @nestjs/common module. It has a single method named transform() that takes in value as a parameter — postID in this case.
Why Build React Applications with TypeScript
With the method above, any HTTP request from the frontend of this application with a postID that can’t be found in the database will be regarded as invalid. After creating both the service and controller, you need to set up the Post model that is based on the BlogSchema. This configuration could be set up within the root ApplicationModule, but in this instance building, the model in BlogModule will maintain your application’s organization.
Open the ./src/blog/blog.module.ts and update it as shown here:. This module uses the MongooseModule.forFeature() method to define which models should be registered in the module. Without this, injecting the PostModel within the BlogService using @injectModel() decorator wouldn’t work.
Create your NestJS Application
Go back to the terminal and test the application by running the following command from the terminal while you are still within the project’s directory:. The preceding command will run the application in watch mode to track any new changes while the application is still running. Note: Ensure that the MongoDB instance is still running from the other terminal as mentioned earlier. Otherwise, open another terminal and run sudo mongod to start the MongoDB process in the background.You can use Postman to test the API. Postman is a testing tool to confirm and check the behavior of your web service before deploying to production.
As shown here, a POST HTTP call was made to http://localhost:5000/blog/post endpoint with the details of a blog post.
After a successful process, the created post was returned as a response with a message indicating that it was created successfully.
From the screenshots above, it is obvious that all the implemented logic for your backend API is working properly, but this raises another concern. Any random user can easily make an API call to retrieve or create a new post without authentication and succeed. This is not acceptable as the applications need to be smart enough to manage the identity of users.
To make the API protected, first, you will need an Auth0 account, create a new account if you don’t have one already. Try out the most powerful authentication platform for free.Get started →. After creating an account, log in to it and head over to the API section of your Auth0 management dashboard and select APIs from the side menu. This will show you your list of APIs if you have created any for your account, but for this tutorial, go ahead and click on the CREATE API button and set up a new one.
Next, provide a friendly name as you deem fit for your API. Set the identifier as http://localhost:5000/api. This will be used as the audience later when configuring the access token.
Once you are done, leave the signing algorithm as RS256, as it is the best option from the security point of view, and click on the CREATE button to proceed:.
Immediately when you created this API, Auth0 also automatically created a test application for you to use. This is a machine-to-machine application that you will only use here for testing purposes for this part of the tutorial. You will create a new single-page application that will represent the front-end React app in the next part of the tutorial. Next, click on "Applications" from the side menu to view the list of applications for your account and select the test application that was generated.