This Dot Blog
This Dot provides teams with technical leaders who bring deep knowledge of the web platform. We help teams set new standards, and deliver results predictably.
How to host a full-stack app with AWS CloudFront and Elastic Beanstalk
You have an SPA with a NestJS back-end. What if your app is a hit? You need to be prepared to serve thousands of users? You might need to scale your API horizontally, which means you need to have more instances running behind a load balancer....
Sep 11, 2023
Setting Up TypeORM Migrations in an Nx/NestJS Project
Feb 22, 2023
Combining Validators and Transformers in NestJS
Oct 27, 2022
NestJS API Versioning Strategies
Versioning is an important part of API design. It's also one of those project aspects that is not given enough thought upfront, and it often happens that it comes into play late in the game, when it's difficult to introduce breaking changes (and introducing versioning can sometimes be a breaking change). In this blog post, we will describe the various versioning strategies that you can implement in NestJS, with a special focus on the highest-matching version selection. This is a strategy that you might consider when you want to minimize the amount of changes needed to upgrade your API-level versions. Types of versioning In NestJS, there are four different types of versioning that can be implemented: * URI versioning * The version will be passed within the URI of the request. For example, if a request comes in to /api/v1/users, then v1 marks the version of the API. * This is the default in NestJS. * Custom header versioning * A custom request header will specify the version. For example, X-API-Version: 1 in a request to /api/users will request v1 version of the API. * Media type versioning * Similar to custom header versioning, a header will specify the version. Only, this time, the standard media accept header is used. For example: Accept: application/json;v=2 * Custom versioning * Any aspect of the request may be used to specify the version(s). A custom function is provided to extract said version(s). * For example, you can implement query parameter versioning using this mechanism. URI versioning and custom header versioning are the most common choices when implementing versioning. Before deciding which type of versioning you want to use, it's also important to define the versioning strategy. Do you want to version on the API level? Or on the endpoint level? If you want to go with the endpoint-versioning approach, this gives you more fine-grained control over your endpoints, without needing to reversion the entire API. The downside of this approach, is that it may get difficult to track endpoint versions. How would an API client know which version is the latest, or which endpoints are compatible with each other? There would need to be a discovery mechanism for this, or just very well maintained documentation. API-level versioning is more common, though. With API-level versioning, every time you introduce a breaking change, you deliver a new version of the entire API, even though internally, most of the code is unchanged. There are some strategies to mitigate this, and we will focus on one in particular in this blog post. But first, let's see how we can enable versioning on our API. Applying versions to your endpoints The first step is to enable versioning on the NestJS application: ` With URI versioning enabled, to apply a version on an endpoint, you'd either provide the version on the @Controller decorator to apply the version to all endpoints under the controller, or you'd apply the version to a route in the controller with the @Version decorator. In the below example, we use endpoint versioning on the findAll() method. ` We can invoke findAll() using curl: ` How can we invoke findOne(), though? Since only findAll() is versioned, invoking findOne() needs to be without a version. When you request an endpoint without a version, NestJS will try to find so-called "version-neutral" endpoints, which are the endpoints that are not annotated with any version. In our case, this would mean the URI we use will not contain v1 or any other version in the path: ` This happens because implicitly, NestJS considers the "version-neutral" version to be the *default version* if no version is requested by the API client. The default version is the version that is applied to all controllers/routes that don't have a version specified via the decorators. The versioning configuration we wrote earlier could have easily been written as: ` Meaning, any controllers/routes without a version (such as findAll() above), will be given the "version-neutral" version by default. If we don't want to use version-neutral endpoints, then we can specify some other version as the default version. ` The findOne() endpoint will now return a 404, unless you call it with an explicit version. This is because we no longer have any "version-neutral" versions defined anywhere (the controllers/routes or the defaultVersion property). ` Multiple versions Multiple versions can be applied to a controller/route by setting the version to be an array. ` Invoking /api/v1/users or /api/v2/users will both land on the same method findAll() in the controller. Multiple versions can also be set in the defaultVersion of the versioning configuration: ` This simply means that controllers/routes without a version decorator will be applied to both version 1 and version 2. Selection of highest-matching version Imagine the following scenario: You've decided to use API-level versioning, but you don't want to update all of your controllers/routes every time you increase a version of the API. You only want to do it on those that had breaking changes. Other controllers/routes should remain at whatever version they are currently. Currently, in NestJS, there is no way of accomplishing this with just a configuration option. But fortunately, the versioning config allows you to define a custom version extractor. A version extractor is simply a function that will tell NestJS *which versions the client is requesting*, in order of preference. For example, if the version extractor returns an array such as ['3', '2', '1']. This means the client is requesting version 3, or version 2 if 3 is not available, or version 1 if neither 2 nor 3 is available. This kind of highest-matching version selection does have a caveat, though. It does not reliably work with the Express server, so we need to switch to the Fastify server instead. Fortunately, that is easy in NestJS. Install the Fastify adapter first: ` Next, provide the FastifyAdapter to the NestFactory: ` And that's it. Now we can proceed onto writing the version extractor: ` The version extractor uses the x-api-version header to extract the requested version and then returns an array of all possible versions up to and including the requested version. The reason why we chose to use header-based versioning in this example is that it would be too complex to implement URI-based versioning using a version extractor. First of all, the version extractor gets an instance of FastifyRequest. This instance does not provide any properties or methods for obtaining parts of the URL. You only get the URL path in the request.url property. You would need to parse this yourself if you wanted to extract a route token or a query parameter. Secondly, you would also need to handle the routing based on the version requested. Now, if we add multiple versions to our controller, we will always be getting the highest supported version: ` Let's test this: ` We have only one findOne() implementation, which doesn't have any explicit version applied. However, since the default version is 1 (as configured in the versioning config), this means that version 1 applies to the findOne() endpoint. Now, if a client requested version 2 of our API, the version extractor would tell NestJS to first try version 2 of the endpoint if exists, or to try version 1 if it doesn't exist. Unlike findOne(), findAll1() and findAll2() have explicit versions applied: version 1 and version 2, respectively. That's why the third and the fourth calls will return the versions that were explicitly requested by the client. Conclusion This was an overview of the tools you have at your disposal for implementing various versioning strategies in NestJS, with a special focus on API-level versioning and highest-matching version selection. As you can see, NestJS provides a very robust way of implementing various strategies. But some come with caveats, so it is always good to know them upfront before deciding which versioning strategy to use in your project. The entire source code for this mini-project is available on GitHub, with the code related to highest-matching version implementation being in the highest-matching-version-selection branch....
Aug 10, 2022
Introduction to RESTful APIs with NestJS
Introduction on RESTful API with NestJS, covering topics such as module organization, service and controller implementation, testing with Insomnia, logging, Swagger documentation, and exception handling....
Sep 15, 2021
Deploying Nx workspace based Angular and NestJS apps to Heroku
Deploying Angular and NestJS apps to Heroku in an Nx workspace In previous articles, I've shown you how to create an Nx Workspace with Angular and NestJS applications in it. After the applications are ready, we need to host them somewhere. Heroku is one of the services that lets us deploy applications easily. In this article, I'll demonstrate how to deploy the Angular and NestJS applications that are developed using an Nx monorepo. You can find the example code with the aforementioned applications in my GitHub repository. To follow this article, please fork this repo, clone it locally and checkout nxDeployHeroku_entryPoint. ` Install Heroku CLI To follow this article, you need to have the Heroku CLI installed. Please follow the official installation instruction on the Heroku documentation page here. After you install the CLI, type the following command to log in to Heroku: ` Deploying NestJS app We're going to start with deploying the NestJS application. The first thing we need to do is creating a Heroku application. Because you need to come up with a unique application name in all the examples, I'll be using ktrz- prefix for the app names. Please replace it with your own prefix so that the application names don't collide with each other. To create a Heroku application we can use the following command: ` Now we need to configure the application to use Node for building the application. This is what buildpacks are for. To add a buildpack, the heroku buildpacks:add command can be used: ` Heroku uses a Procfile file to specify the commands that are executed on application startup. The default configuration allows for only one *Procfile* and it has to be in the repository root. For it to work with the monorepo with multiple applications, we need a way to configure multiple *Procfiles* in the repository. For this purpose, a multi-procfile buildpack can be used. We can add it using a similar command to the previous one: ` Now we can create a *Procfile* and place it in the directory that makes sense for the monorepo. Let's create the following file: ` apps/photo/api/Procfile To let the buildpack know about the location of the *Procfile*, we need to set PROCFILE env variable for the Heroku application. We can do it using the following command: ` By default, Heroku uses the build script from the package.json file to build the application. We need a more customizable way of building an application so we can configure which application in a monorepo to build. By defining a heroku-postbuild script, we tell Heroku to not use a default build one and use our custom script instead. Let's create the following script: ` package.json As you can see, the PROJECT_NAME env variable is used to determine which application to build. It needs to be configured on the Heroku environment: ` What is left to do is push the changes to a branch and configure Heroku app to use the repository as a source for deployment: ` To configure the Heroku app, go to the dashboard and choose the application that you've created before: Next, navigate to the Deploy tab, choose the GitHub method, search for your repository, and click Connect: Finally, on the bottom, you can choose to deploy manually from the branch that you've created a moment ago: - in package.json add script: ` package.json ` To learn more about *heroku-buildpack-nodejs* and *heroku-buildpack-multi-procfile* configuration, please visit the official documentation: - heroku-buildpack-nodejs - heroku-buildpack-multi-procfile Deploying Angular app Deploying an Angular app has a lot of similar steps. ` The Angular application can be served as just static files with routing configured to always point to the root index.html and let Angular handle the rest. We can use another buildpack to accomplish that. ` *heroku-buildpack-static* is configured via static.json file. We can do a basic configuration like so: ` static.json The example Angular application is configured to use /api proxy for the backend. This also can be configured within *static.json* file: ` static.json The last thing to do is configure Heroku to use the static buildpack via the Procfile: ` apps/photo/fe/Procfile To learn more about *heroku-buildpack-static* configuration, please visit the oficial documentation here. Let's commit the changes and configure the second app to use the same GitHub repository: - Go to dashboard and choose the frontend application that you've created before. - Next, navigate to the Deploy tab, choose GitHub method, search for your repository, and click Connect. - Finally, on the bottom you can choose to deploy manually from the branch that you've pushed to a moment ago. After all those steps, you can navigate to your deployed app: Summary If you want to see the result code, you can find it on my GitHub repository. In case you have any questions, you can always tweet or DM me @ktrz. I'm always happy to help!...
May 25, 2021
Nx Workspace with Angular and Nest
Nx Workspace with Angular and Nest In a previous article, we covered creating an Angular project with Nx monorepo tooling. This gives us a great base, but usually, our application will need a server-side project to feed our frontend application with all of the necessary data. Why not leverage the monorepo approach for this use-case then? In this article, I would like to show you how to bring Nest server-side application that will serve our frontend application all the necessary data and behaviors. We will build on top of the existing Nx-based Angular application, which you can find in this GitHub repository. If you want to follow the code in this article, I recommend cloning this repository and checking out new branch with the nxAngularNest_entryPoint tag. ` The application in the aforementioned repository contains a simple application that displays a list of photos that can be either liked or disliked. If you run the code initially, you'll notice that the app requires a backend server from which to pull the necessary data. We will build this simple backend application using the Nest framework, and all of that within a single monorepo project, so that it is easier to manage both applications. Nest Overview Nest is a backend framework for building scalable Node applications. It is a great tool for Angular devs to get into server-side development as it is based on concepts that are very similar to Angular ones: - TypeScript support - Dependency Injection mechanism that is very similar to the Angular mechanism - Puts emphasis on testability - Configuration is similar (mostly based on decorators) - Best practices and conventions are similar - knowledge is transferable All of this makes for a great candidate to use Nest as a server application framework for our application. Let's add a Nest application to our existing project. Add Nest app To start off, we need to install all of the dependencies which will allow Nx to assist us with building a Nest application. All of this is packed into a single Nx plugin @nrwl/nest. ` With the tooling in place, we can generate the Nest application with one command. ` Please keep in mind that, since we're keeping applications using 2 separate Nx plugins, we need to specify the full path to the schematics for generating applications/libraries. In this case, it is @nrwl/nest:application A nice feature when creating a Nest application is the ability to set up a proxy to our newly created application so that our FE application can easily access it. We can use the --frontendProject additional param to do so. Let's use it to create our actual Nest application: ` This command will generate a project skeleton for us. The application is bootstrapped similarly to an Angular app. We define an AppModule, which will be a root of the app, and all the other necessary modules will be imported within this module. ` ` For a more in-depth explanation of the Nest framework, please visit the official docs. Building the API For our photos application we require 3 following endpoints to be handled: GET /api/photos - which returns the list of all photos PUT /api/photos/:photoId/like - allows us to like a photo PUT /api/photos/:photoId/dislike - allows us to dislike a photo To handle requests in Nest, we use a class called Controller which can handle requests to a specific sub-path (in this case it will be the photos sub-path). To keep our application clean, let's create a separate module that will contain our controller and all the necessary logic. `` nx g @nrwl/nest:module app/photos --project=api-photos nx g @nrwl/nest:controller app/photos --project=api-photos --export `` Since the controller shouldn’t contain business logic, we will also create a service to handle the logic for storing and manipulating our photo collection. `` nx g @nrwl/nest:service app/photos --project=api-photos `` Our newly created service will be added to our PhotosModule providers. ` Just like in Angular, we also need to include our PhotosModule in the AppModule's imports to notify Nest of our module's existence. ` Now, we are ready to build the API we need. We can start with the first endpoint for getting all the photos: GET /api/photos Let's start by creating all the necessary logic within the PhotosService class. We need to store our collection of photos and be able to return them in a form of an Array. To store it, I prefer to use an id-based map for quick access. ` To simplify transformation from a map to an array, I added a utility function stateToArray. It can definitely be extracted to a separate file/directory as an application grows, but for now, let's leave it here inline. Now, our controller can leverage this getPhotos function to return a list of all photos via an API. To create an endpoint in Nest, we use decorators corresponding to an HTTP method that we want to expose. In our case, it will be a GET method so we can use a @Get() decorator: ` Now, we can run both our frontend and backend server to see the list of photos requested via our new API. ` ` We still need to implement the liking and disliking feature in the Nest app. To do this, let's follow the same approach as we did earlier. First, let's add the liking functionality to PhotosService: ` and similarly, we can implement the dislike functionality ` With both methods in place, all that is left to do is implement to endpoints in the PhotosController and use methods provided by a PhotosService: ` The path params are defined analogously to how we define params in Angular routing with the : prefix, and to access those params we can use @Param() decorator for a method's parameter. Now, after our server reloads, we can see that the applications are working as expected with both the liking and disliking functionalities working. Common interfaces In this final section, I would like to show you how we can benefit from the monorepo approach by extracting the common interface between the frontend and backend to a separate library. Let's start by creating a library, again using the Nx command tools. `` nx g @nrwl/workspace:library photo/api `` This will generate a new library under libs/photo/api/ folder. Let's create a new file libs/photo/api/src/lib/photo.model.ts and put the ApiPhoto interface in it so it can be shared by both frontend and backend applications. ` We need to export this interface in the index.ts file of the library as well: ` There is no way we can use the same interface for an API request in both of our applications. This way, we make sure that the layer of communication between our applications in always up to date. Whenever we change the structure of the data in our server application, we will have to apply the appropriate changes to the frontend application as well as the TypeScript compiler. This forces data to be consistent and braking changes to be more manageable. Conclusion As you can see, maintaining the project in a monorepo makes it easier to maintain. Nest framework is a great choice for a team of developers that are acquainted with Angular as it builds on top of similar principles. All of that can be easily managed by the Nx toolset. You can find the code for this article's end result on my GitHub repo. Checkout the nxAngularNest_ready tag to get the up-to-date and ready-to-run solution. To start the app you need to serve both Angular and Nest projects: ` ` In case you have any questions you can always tweet or DM me @ktrz. I'm always happy to help!...
Apr 7, 2021
Build an API Gateway with NestJs in 10 minutes
This article intention is to give the reader broader perspective into the Microservices. It also covers the concept of API Gateway and how to implement your own in NestJs.....
Jan 15, 2020
Reducing Mental Fatigue: NestJS + ObjectionJS
Oct 28, 2019