<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Christian Balola]]></title><description><![CDATA[Entrepreneur & Software Engineer | JavaScript | NodeJs | AWS | Blockchain | Web3 | DAPP]]></description><link>https://blog.chrisbalola.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 05 Jun 2026 21:03:51 GMT</lastBuildDate><atom:link href="https://blog.chrisbalola.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Deploying a NestJS API to AWS Elastic Beanstalk with CI/CD implementation]]></title><description><![CDATA[I recently published a hands-on article about building a secure NodeJS REST API with NestJS, in which I showed a simple way of deploying the API built to Heroku.
In this tutorial, we’ll explore a simple way of deploying the same API to AWS while impl...]]></description><link>https://blog.chrisbalola.com/deploying-a-nestjs-api-to-aws-elastic-beanstalk-with-cicd-implementation</link><guid isPermaLink="true">https://blog.chrisbalola.com/deploying-a-nestjs-api-to-aws-elastic-beanstalk-with-cicd-implementation</guid><category><![CDATA[nestjs]]></category><category><![CDATA[AWS]]></category><category><![CDATA[CodePipeline]]></category><category><![CDATA[Elastic Beanstalk]]></category><category><![CDATA[ci-cd]]></category><dc:creator><![CDATA[Christian Balola]]></dc:creator><pubDate>Thu, 04 Aug 2022 16:01:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659628674268/FHBub5iAS.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently published a hands-on article about <a target="_blank" href="https://chrisbalola.com/building-a-secure-and-quality-nodejs-rest-api-with-nestjs">building a secure NodeJS REST API with <a target="_blank" href="https://nestjs.com/">NestJS</a></a>, in which I showed a simple way of deploying the API built to Heroku.</p>
<p>In this tutorial, we’ll explore a simple way of deploying the same API to AWS while implementing <a target="_blank" href="https://semaphoreci.com/cicd">CI/CD</a> (Continuous Integration / Continuous Delivery) practices so that deployment stays smooth for our future releases.</p>
<h2 id="heading-what-well-use">What we’ll use</h2>
<p>Let’s quickly outline the tools and services we’re gonna use to deploy our API.</p>
<ul>
<li><a target="_blank" href="https://aws.amazon.com/elasticbeanstalk/">AWS Elastic Beanstalk</a>: A service for deploying and scaling web applications and services developed with Java, .NET, PHP, Node.js, Python, Ruby, Go, and Docker on familiar servers such as Apache, Nginx, Passenger, and IIS.</li>
<li><a target="_blank" href="https://aws.amazon.com/rds/">AWS RDS</a> (Relational Database Service): A collection of managed services that makes it simple to set up, operate, and scale relational databases in the cloud. It supports some of the most popular database management systems including PostgreSQL that we’re going to use.</li>
<li><a target="_blank" href="https://aws.amazon.com/codepipeline/">AWS CodePipeline</a>: A continuous delivery service that helps you automate your release pipelines. It’s the one we shall use to automate the deployment of our app from GitHub to Elastic Beanstalk.</li>
<li><a target="_blank" href="https://aws.amazon.com/codebuild/">AWS CodeBuild</a>: A continuous integration service that compiles source code, runs tests, and produces software packages that are ready for deployment. It will be used by CodePipeline to build our code after fetching it from GitHub and before deploying it to Elastic Beanstalk.</li>
</ul>
<p>For the sake of simplicity, in this tutorial, I chose to deploy our API to AWS Elastic Beanstalk using AWS CodePipeline and AWS CodeBuild; but there are many target AWS services (AWS ECS, AWS EC2, etc.) that we could use to deploy our API and many different ways to go about it (Using GitHub Actions, Jenkins, Docker, etc.).</p>
<h2 id="heading-lets-get-our-hands-dirty">Let’s get our hands dirty</h2>
<h3 id="heading-step-1-preparing-the-project-for-deployment">Step 1: Preparing the project for deployment</h3>
<p>You can find the project’s code <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api">here</a>. We shall clone it, and create a new branch on which we shall work to prepare the project for deployment to AWS.</p>
<p>After switching to a new branch using <code>git checkout -b awscicd</code>, go to <code>src/database/datasource.ts</code> and update the file such that we use a URL instead of separate database connection parameters (hostname, database name, port, username, and password).</p>
<pre><code><span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
<span class="hljs-keyword">import</span> { <span class="hljs-title">DataSource</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'typeorm'</span>;

export const AppDataSource <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> DataSource({
  <span class="hljs-keyword">type</span>: <span class="hljs-string">'postgres'</span>,
  url: process.env.DB_URL,
  entities: [<span class="hljs-string">'./**/*.entity.js'</span>],
  synchronize: <span class="hljs-literal">false</span>,
  migrations: [<span class="hljs-string">'src/database/migrations/*{.ts,.js}'</span>],
  migrationsRun: <span class="hljs-literal">true</span>,
  ...(process.env.DB_SSL <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'true'</span>
    ? {
      ssl: <span class="hljs-literal">true</span>,
      extra: {
        ssl: {
          rejectUnauthorized: <span class="hljs-literal">false</span>,
        },
      },
    }
    : {}),
});
AppDataSource.initialize()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    console.log(<span class="hljs-string">"Data Source has been initialized!"</span>)
  })
  .catch((err) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    console.error(<span class="hljs-string">"Error during Data Source initialization"</span>, err)
  });
</code></pre><p>Next, update the database module such that it looks like the code below:</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">Module</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ConfigModule</span>, <span class="hljs-title">ConfigService</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/config'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">TypeOrmModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/typeorm'</span>;

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> ({
        <span class="hljs-keyword">type</span>: <span class="hljs-string">'postgres'</span>,
        url: configService.get(<span class="hljs-string">'DB_URL'</span>),
        autoLoadEntities: <span class="hljs-literal">true</span>,
        synchronize: <span class="hljs-literal">false</span>,
        migrationsRun: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Will run migrations every time the app starts</span>
        migrations: [<span class="hljs-string">'dist/database/migrations/*.js'</span>], <span class="hljs-comment">// Links to the migrations (in /dist because: after build)</span>
        ...(configService.get(<span class="hljs-string">'DB_SSL'</span>) ? {
          ssl: <span class="hljs-literal">true</span>,
          extra: {
            ssl: {
              rejectUnauthorized: <span class="hljs-literal">false</span>
            }
          },
        } : {})
      }),
    }),
  ],
})
export class DatabaseModule {}
</code></pre><p>Inside  <code>app.module.ts</code> file, Update the configuration module’s validation schema like below:</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">Module</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">AppController</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./app.controller'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">AppService</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./app.service'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ConfigModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/config'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">DatabaseModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./database/database.module'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">UsersModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./users/users.module'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">AuthModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./auth/auth.module'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">TransactionsModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./transactions/transactions.module'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">Joi</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'joi'</span>;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: <span class="hljs-literal">true</span>,
      envFilePath: <span class="hljs-string">'.env'</span>,
      validationSchema: Joi.object({
        NODE_ENV: Joi.string().default(<span class="hljs-string">'development'</span>),
        DB_URL: Joi.string().required(),
        DB_SSL: Joi.boolean().default(<span class="hljs-literal">false</span>),
      }),
    }),
    DatabaseModule,
    UsersModule,
    AuthModule,
    TransactionsModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
</code></pre><p>Next, in your project root directory, create a <code>buildspec.yml</code> file with the following content:</p>
<pre><code><span class="hljs-attribute">version</span>: 0.2

<span class="less"><span class="hljs-attribute">phases</span>:
  <span class="hljs-attribute">build</span>:
    <span class="hljs-attribute">commands</span>:
       - npm ci &amp;&amp; npm run build # clean install (npm ci) and build (npm run build)

<span class="hljs-attribute">artifacts</span>:
  <span class="hljs-attribute">files</span>:
    - <span class="hljs-string">'**/*'</span></span>
</code></pre><blockquote>
<p>The <code>buildspec.yml</code> file is how we let <strong>AWS CodeBuild</strong> know which commands to run to build our project.</p>
</blockquote>
<p>Then, make sure you have a file named <code>Procfile</code> (with no extension) at the root of your project with the following content: <code>web: npm run start:prod</code>.</p>
<blockquote>
<p>AWS Elastic Beanstalk will use the command inside the <code>Procfile</code> to start our app once deployed.</p>
</blockquote>
<p>Once all the above updates are made, you may push the project to GitHub. You may find my implementation <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api/tree/awscicd">here</a>.</p>
<h3 id="heading-step-2-sign-in-to-aws">Step 2: Sign in to AWS</h3>
<p>One needs to sign in to do anything with AWS, right?😇 If you don’t have an account, you may sign up by going to the <a target="_blank" href="https://aws.amazon.com/">AWS website</a> and clicking on “<strong>Create an AWS account</strong>”.</p>
<h3 id="heading-step-3-create-a-database-with-rds">Step 3: Create a database with RDS</h3>
<p>Once signed in, type “<strong>RDS</strong>” on the search bar and click on the first result to open its main page, then click on “<strong>Create database</strong>”.</p>
<p>You’ll be greeted with a dashboard that looks like this:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659626734389/EXS2Nu3Aa.png" alt="image.png" /></p>
<p>From this page, select “<strong>PostgreSQL</strong>” as the engine type and select “<strong>Free tier</strong>” as the template.</p>
<blockquote>
<p><strong>Note</strong>: Our API’s migrations were generated for a <strong>PostgreSQL</strong> database. If you choose a different engine, an error will be triggered unless you generate them for the new engine in your local environment. You can also ignore the migrations by setting <code>migrationsRun</code> to <code>false</code> and <code>synchronize</code> entities by setting <code>synchronize</code> to <code>true</code> (this is not recommended in production though) in the <code>database.module.ts</code> file.</p>
</blockquote>
<p>Under settings, fill in the database instance name, and a master username and password (which must be kept somewhere safe).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659626869743/UZHt3etM4.png" alt="image.png" /></p>
<p>Next, scroll down to “<strong>Connectivity</strong>”, and, under “<strong>Public access</strong>”, select “<strong>Yes</strong>” to allow the database to be accessible outside its virtual private cloud.</p>
<p>Under “<strong>VPC Security Group</strong>”, select “<strong>Create new</strong>” to create a new security group for your VPC (Virtual Private Cloud)’s database instance. Or simply choose one if already created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659626907464/VNtKzZDsP.png" alt="image.png" /></p>
<blockquote>
<p><strong>Note</strong>: You may also stick to the default VPC security group by selecting “Choose existing” and making sure there’s the default security group there.</p>
</blockquote>
<p>Next, scroll down to “<strong>Additional configuration</strong>”, expand it, and fill in your initial database name like in the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659626946709/sLWx_LpZh.png" alt="image.png" /></p>
<p>Then click on “<strong>Create database</strong>” and wait for your database to be ready. You’ll know it’s ready when its status becomes “<strong>available</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659626983913/cs-BGvjBl.png" alt="image.png" /></p>
<h3 id="heading-step-4-update-the-vpc-security-group">Step 4: Update the VPC security group</h3>
<p>We need to update the database instance’s security group in order to make sure our Elastic Beanstalk application can access it. We do so by adding to it, an inbound rule that allows access from any IP (or a specific one).</p>
<p>Click on the database identifier, then under the “<strong>Security</strong>” section of the “<strong>Connectivity &amp; Security</strong>” tab, click on the VPC security group we had chosen when creating our database.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627034870/aAoeRMo7n.png" alt="image.png" /></p>
<p>Next, scroll down to “<strong>Inbound rules</strong>” and click on “<strong>Edit inbound rules</strong>”. On the page opened, click on “<strong>Add rule</strong>”, update the added row like on the image below and click on “<strong>Save rules</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627067114/Vy1GpsjBF.png" alt="image.png" /></p>
<h3 id="heading-step-5-create-elastic-beanstalk-application">Step 5: Create Elastic Beanstalk Application</h3>
<p>Type “<strong>Elastic</strong>” in the search bar and click on “<strong>Elastic Beanstalk</strong>”. You will be greeted by Elastic Beanstalk’s beautiful welcome page, on which you may locate the “<strong>Create Application</strong>” button and click on it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627127571/Z2g98Cg47.png" alt="image.png" /></p>
<p>On the page that opens up, fill in the name of the application, scroll down, select “<strong>Node.js</strong>” as the platform, then click on “<strong>Create application</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627151508/zNxf0p3O2.png" alt="image.png" /></p>
<blockquote>
<p><strong>Note</strong>: Under application code, we chose “<strong>Sample application</strong>” to keep it simple, but you can upload your code (zipped) by selecting “<strong>Upload your code</strong>” instead. That being said, I wouldn’t advise doing so unless you know what you are doing, you may run into problems and Elastic Beanstalk isn’t very good at saying what’s wrong.</p>
</blockquote>
<p>Once your application has been created, an environment is created alongside it and a dashboard such as the one on the image below appears:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627184388/Ft74LThvu.png" alt="image.png" /></p>
<p>Below the name of the environment, is the link to the application; If you click on it at this stage, you will see the sample app AWS has provisioned for our Elastic Beanstalk application.</p>
<h3 id="heading-step-6-update-environment-variables">Step 6: Update Environment Variables</h3>
<p>On the left menu of the Elastic Beanstalk application dashboard, you should be able to see a menu item with the name “Configuration”. Click on it to open the configuration section of the app’s environment.</p>
<p>Next, on the “<strong>Software</strong>” category, click on “<strong>Edit</strong>” and then scroll down to “<strong>Environment properties</strong>”. Fill the environment properties like on the image below then click on “<strong>Apply</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627233086/pWGrNuS7d.png" alt="image.png" /></p>
<blockquote>
<p><strong>Note</strong>: Please take a good look at the database URL’s structure. Replace the username and password with the values you kept in a safe place, and “dbname” with the initial database name as set in step 3.</p>
</blockquote>
<p>The endpoint and the port of the database can be found on the database instance’s page which you can find by typing “<strong>RDS</strong>” in the search bar, clicking on the first result, clicking on databases, then on the name of the database instance. They should be available under the “<strong>Endpoint &amp; port</strong>” section of the “<strong>Connectivity &amp; security</strong>” tab.</p>
<h3 id="heading-step-7-setup-codepipeline">Step 7: Setup CodePipeline</h3>
<p>Search for the <strong>CodePipeline</strong> service in the search bar and open its page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627330015/nJ--fY5ik.png" alt="image.png" /></p>
<p>Click on “<strong>Create pipeline</strong>”, fill in a name for the pipeline (Eg: accounting-api-pipeline), then click on next.</p>
<p>At the “<strong>Source stage</strong>”, select “<strong>GitHub (Version 2)</strong>” as the source provider. You will see a page like the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627427309/B3cPVvJqY.png" alt="image.png" /></p>
<p>Assuming you don’t already have a connection, click on “<strong>Connect to GitHub</strong>”, fill in a name for the connection in the opened window like in the image below, then click on “<strong>Connect to GitHub</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627458053/07O23TkTr.png" alt="image.png" /></p>
<p>A page where you can either select an app or install a new GitHub app will open. Click on “<strong>Install a new app</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627472919/9V9j70wBM.png" alt="image.png" /></p>
<p>You will be redirected to GitHub, and from there you can select which repositories your AWS pipeline will be able to access on your GitHub account via this connection. Once an app has been installed, click on “<strong>Connect</strong>”; the window will close and you may get back to the pipeline creation page on which you may select the name of the repository to pick the code from and the branch.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627509498/oB4ga-Qr_.png" alt="image.png" /></p>
<p>That being done, click on “<strong>Next</strong>”, and on the “<strong>Build stage</strong>”, select “<strong>AWS CodeBuild</strong>”, then click on “<strong>Create project</strong>”. It will open a new window like in the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627546214/FhYLgD4ug.png" alt="image.png" /></p>
<p>Simply fill in a name for the “<strong>Build project</strong>” and select the operating system, the runtime, and the image, then scroll down and click on “<strong>Continue to pipeline</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627639012/DNW-9qsUK.png" alt="image.png" /></p>
<p>Once the “<strong>Build project</strong>” is selected, click on “<strong>Next</strong>”, then on the Deploy stage, select “<strong>AWS Elastic Beanstalk</strong>” as the deploy provider.</p>
<p>Two input fields will appear where you will select the application’s name and the environment to deploy to.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627664187/AHJdwCcMC.png" alt="image.png" /></p>
<p>Click on “<strong>Next</strong>” and you should be able to see a summary of all the stages as configured. You may then click on “<strong>Create pipeline</strong>” to create the pipeline.</p>
<blockquote>
<p><strong>Note</strong>: Once created, the pipeline will immediately initiate a release from GitHub, build it, then deploy it to your AWS Elastic Beanstalk application’s environment.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659627720546/fCyRlrcuH.png" alt="image.png" /></p>
<p>This is the page to monitor your pipeline from. You can always find it in your console by searching for “<strong>CodePipeline</strong>”, clicking on “<strong>pipelines</strong>” and then selecting the name of the pipeline you want to inspect.</p>
<p>As you may see, the dashboard shows you the status of each stage and the last time it changed. You may initiate a release by clicking on “<strong>Release change</strong>”. You would probably not need to do it since AWS will be doing it for you every time there’s a change on the GitHub repository’s branch that you connected to your pipeline.</p>
<p>Once the deployment is done, the Elastic Beanstalk app will start the Nest app and run migrations while at it.</p>
<blockquote>
<p><strong>Note</strong>: Browser seems to load the Swagger UI page’s resources through <code>https</code>, yet we haven’t added a certificate to our Elastic Beanstalk application. That and the fact that Swagger UI is served via redirection of its resources to a specified path, may explain why Swagger UI page does not display on some browsers. The easiest solution for this is to add <code>https</code> to our application (a tutorial for another time).</p>
</blockquote>
<p>That being said, our Elastic Beanstalk app can already receive requests at the endpoint specified on its main page.</p>
<h2 id="heading-debugging-failure">Debugging failure</h2>
<p>From now on, if you make changes to your code, push it to the GitHub branch that’s connected to the AWS pipeline and it will trigger continuous integration and deployment of the app to AWS Elastic Beanstalk.</p>
<p>However, a failure can happen at any stage of the CI/CI process, most likely the “<strong>Build stage</strong>” of the pipeline, the “<strong>Deploy stage</strong>” of the pipeline, and the elastic beanstalk app stage.</p>
<p>If the error occurs at:</p>
<ul>
<li><strong>The pipeline’s “Build stage”</strong>: On the pipeline’s page, under the “<strong>Build</strong>” section, click on “<strong>Details</strong>”, and read the “<strong>Build logs</strong>” to find out what happened.</li>
<li><strong>The pipeline’s “Deploy stage”</strong>: Under the “<strong>Deploy</strong>” section, near the text that displays the last release time, there should be a text that you can click on, and it will redirect you to a page where you can read the cause of the error.</li>
<li><strong>The app level</strong>: The Elastic Beanstalk application’s dashboard may display the state of “<strong>Health</strong>” as “<strong>Warning</strong>” or “<strong>Degraded</strong>”. To find out what may have caused the app’s failure, click on “<strong>Logs</strong>” on the left menu, then on “<strong>Request Logs</strong>” select “<strong>Last 100 lines</strong>”. On the logs row that appears, click on “<strong>Download</strong>” to read them. In the logs page opened, you may want to focus on the ones under the section <code>/var/log/web.stdout.log</code>.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As a software developer, it’s very important to get familiar with cloud providers like <a target="_blank" href="https://aws.amazon.com/">AWS</a>, not only because of the numerous advantages they provide (flexibility, scalability, etc.) but also because they are included in the tech stack of numerous large companies.</p>
<p>This tutorial is my way of showing you a simple entry point to the world of cloud providers, especially AWS; but just like every entry point, they require you to go deeper or you’ll stay stagnant.</p>
<p>More tutorials will come, but in the meantime please <a target="_blank" href="https://www.simplilearn.com/tutorials/aws-tutorial">read more about AWS</a>, and feel free to get in touch with me in case of anything. I’m often available on <a target="_blank" href="https://twitter.com/chrisbalola">Twitter</a>, <a target="_blank" href="https://github.com/chrisbalola">GitHub</a>, and <a target="_blank" href="https://www.linkedin.com/in/chrisbalola/">LinkedIn</a>.</p>
<p>Kind Regards,</p>
<p>Christian.</p>
]]></content:encoded></item><item><title><![CDATA[Building a secure and quality NodeJS REST API with NestJS]]></title><description><![CDATA[Besides being secure, a quality API must be: well structured, maintainable, performant, documented, and scalable.
Although many resources talk about the above, it’s hard to find them well aligned together so the developer who’s learning can see a cle...]]></description><link>https://blog.chrisbalola.com/building-a-secure-and-quality-nodejs-rest-api-with-nestjs</link><guid isPermaLink="true">https://blog.chrisbalola.com/building-a-secure-and-quality-nodejs-rest-api-with-nestjs</guid><category><![CDATA[Node.js]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[swagger]]></category><category><![CDATA[typeorm]]></category><dc:creator><![CDATA[Christian Balola]]></dc:creator><pubDate>Mon, 18 Jul 2022 08:06:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658129611480/d0-sJIPaN.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Besides being secure, a quality API must be: well structured, maintainable, performant, documented, and scalable.</p>
<p>Although many resources talk about the above, it’s hard to find them well aligned together so the developer who’s learning can see a clear big picture of how to properly implement them.</p>
<p>In this article, I will share the blueprints for building a <strong>REST API</strong> for a project where security and good structure are essential.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This tutorial assumes you know the basics of programming and have experience in using:</p>
<ul>
<li><strong>TypeScript</strong>: A syntactical superset of JavaScript. To learn TypeScript, read its documentation <a target="_blank" href="https://www.typescriptlang.org/docs/">here</a>.</li>
<li><strong>Node.js &amp; NPM</strong>: In case you don’t know Node.js, the content in the following may help you get started: <a target="_blank" href="https://www.tutorialspoint.com/nodejs/index.htm">Node.js tutorial</a>.</li>
<li><strong>REST APIs</strong>: You may read about what they are <a target="_blank" href="https://www.geeksforgeeks.org/rest-api-introduction/">here</a>.</li>
</ul>
<h2 id="heading-why-nestjs">Why NestJS?</h2>
<p>The following traits make NestJS one of the best development frameworks for NodeJS backend applications:</p>
<ul>
<li><strong>Structure</strong>: Nest has a clean, opinionated, and modular structure. This saves you time and allows you to write well-structured code by default.</li>
<li><strong>Flexibility</strong>: Nest allows you to use either <a target="_blank" href="https://expressjs.com/">Express</a> or <a target="_blank" href="https://www.fastify.io/">Fastify</a>, to code in either TypeScript or JavaScript; to build either a REST API or a GraphQL one, etc. It also supports a majority of ORMs.</li>
<li><strong>Documentation</strong>: The <a target="_blank" href="https://docs.nestjs.com/">Nest documentation</a> is pretty exhaustive and very well streamlined.</li>
<li><strong>Many supported modules</strong>: Nest has ready-to-use wrappers for many NPM modules and utilities; Which makes it super easy to use and helps save the developer’s time. Some of these modules are <a target="_blank" href="https://github.com/nestjs/passport">@nestjs/passport</a> (for authentication) and <a target="_blank" href="https://github.com/nestjs/swagger">@nestjs/swagger</a> (for API documentation).</li>
</ul>
<blockquote>
<p><strong>Note</strong>: This tutorial is just a small guide to building secure REST APIs. Although I’ll try my best to be as explicit as possible, you should read more about NestJS and other technologies used here to understand them better.</p>
</blockquote>
<h2 id="heading-what-well-build">What we’ll build</h2>
<p>Since this is a hands-on tutorial we’re gonna learn while building a super-simple accounting app where a user can sign up, log in, manage transactions and log out.</p>
<p>Here’s what our data model will look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658101816280/-elRApY2I.png" alt="The data model of a simple accounting API" /></p>
<p>A demo of what we’re about to build can be found <a target="_blank" href="https://cb-accounting-app-api.herokuapp.com/">here</a> and the code for this project is entirely available on <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api">this GitHub repository</a>.</p>
<h2 id="heading-steps-for-building-a-quality-and-secure-nestjs-api">Steps for building a quality and secure NestJS API</h2>
<h3 id="heading-step-1-project-setup">Step 1: Project Setup</h3>
<p>First, make sure you have the Nest CLI installed globally. Here’s how to install it:</p>
<pre><code><span class="hljs-built_in">npm</span> i -g @nestjs/cli
</code></pre><p>Now, let’s set up a new NestJS project:</p>
<pre><code>nest <span class="hljs-keyword">new</span> accounting<span class="hljs-operator">-</span>app<span class="hljs-operator">-</span>api
</code></pre><blockquote>
<p><strong>Note</strong>: If you get an error of installing dependencies while running the above command, just run <code>cd accounting-app-api &amp;&amp; npm install</code> to move into the project folder and install dependencies.</p>
</blockquote>
<p>Once the app is generated by the Nest CLI, do the following to install the dependencies of the project:</p>
<pre><code>npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save @nestjs<span class="hljs-operator">/</span>typeorm typeorm pg @nestjs<span class="hljs-operator">/</span>swagger @nestjs<span class="hljs-operator">/</span>config @nestjs<span class="hljs-operator">/</span>passport @nestjs<span class="hljs-operator">/</span>jwt passport passport<span class="hljs-operator">-</span>jwt bcrypt helmet express<span class="hljs-operator">-</span>rate<span class="hljs-operator">-</span>limit dotenv joi class<span class="hljs-operator">-</span>validator class<span class="hljs-operator">-</span>transformer
npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev @types<span class="hljs-operator">/</span>passport<span class="hljs-operator">-</span>jwt @types<span class="hljs-operator">/</span>passport<span class="hljs-operator">-</span>jwt @types<span class="hljs-operator">/</span>bcrypt
</code></pre><p>A brief summary of the technologies used:</p>
<ul>
<li><strong>TypeORM</strong>: The most complete TypeScript ORM solution in my opinion. An ORM (Object-relational mapper) is a programming tool that provides an object-oriented layer between relational databases and object-oriented programming languages without having to write SQL queries; It makes it easy to interact with relational databases.</li>
<li><strong>PostgreSQL</strong>: A free and open-source relational database management system. The <code>pg</code> adapter allows us to interact with our PostgreSQL database.</li>
<li><strong>Swagger</strong>: A set of tools that we’ll use to generate documentation for our API.</li>
<li><strong>NestJS config</strong>: For the API’s configurations.</li>
<li><strong>Joi</strong>: For validation schemas, especially that of the configuration module.</li>
<li><strong>Passport</strong>: For authentication.</li>
<li><strong>Passport-jwt</strong>: For access token generation and verification.</li>
<li><strong>Bcrypt</strong>: For hashing passwords.</li>
<li><strong>Helmet</strong>: Sets HTTP headers appropriately for basic security.</li>
<li><strong>Express-rate-limit</strong>: Allows rate-limiting to protect the API against brute-force attacks</li>
<li><strong>Dotenv</strong>: Loads environment variables from a .env file</li>
<li><strong>Class-validator</strong>: For decorator-based validation on schema definition.</li>
<li><strong>Class-transformer</strong>: For serializing and deserializing objects based on criteria. It’s mostly used internally by <strong>NestJS</strong>’s validation pipe, together with the class-validator.</li>
</ul>
<p>At this stage, the project folder should look like this:</p>
<pre><code>accounting<span class="hljs-operator">-</span>app<span class="hljs-operator">-</span>api
├─ src
│  ├─ app.controller.spec.ts
│  ├─ app.controller.ts
│  ├─ app.module.ts
│  ├─ app.service.ts
│  ├─ main.ts
├─ test
│  ├─ app.e2e-spec.ts
│  ├─ jest.e2e.json
└─ .eslintrc.js
├─ .gitignore
├─ .prettierrc
├─ nest<span class="hljs-operator">-</span>cli.json
├─ package.json
├─ package<span class="hljs-operator">-</span>lock.json
├─ README.md
├─ tsconfig.build.json
├─ tsconfig.json
</code></pre><h3 id="heading-step-2-configuration-and-database-setup">Step 2: Configuration and Database Setup</h3>
<p>We’ll be putting most of our configuration in a <code>.env</code> file. So create a file of name <code>.env</code> in your project directory and paste the following content into it:</p>
<pre><code><span class="hljs-comment"># Create a .env file like this one but with correct values for each variable</span>
<span class="hljs-comment"># -- General</span>
<span class="hljs-attr">NODE_ENV</span>=<span class="hljs-string">"development"</span>
<span class="hljs-attr">PORT</span>=<span class="hljs-number">3000</span>
<span class="hljs-attr">DEBUG</span>=<span class="hljs-literal">true</span>
<span class="hljs-comment"># -- Database</span>
<span class="hljs-attr">DB_HOST</span>=localhost
<span class="hljs-attr">DB_PORT</span>=<span class="hljs-number">5432</span>
<span class="hljs-attr">DB_USERNAME</span>=postgres
<span class="hljs-attr">DB_PASSWORD</span>=your_db_password
<span class="hljs-attr">DB_DATABASE</span>=accounting-app
<span class="hljs-attr">DB_SSL</span>=<span class="hljs-literal">false</span>
<span class="hljs-comment"># The database SSL should be set to true on Heroku</span>
<span class="hljs-comment"># -- JWT</span>
<span class="hljs-attr">JWT_SECRET</span>=YourJwtSecretHere
<span class="hljs-comment"># -- Admin Token</span>
<span class="hljs-attr">ADMIN_ACCESS_TOKEN</span>=YourSecureAdminAccessToken1234567
</code></pre><p>The values should be replaced with the correct ones for each of the environment variables.</p>
<p>Since environment variables are not supposed to be exposed, make sure you add the<code>.env</code> file to your <code>.gitignore</code> file.</p>
<p>Next, run the following command in your project’s folder:</p>
<pre><code>nest g <span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">database</span></span>
</code></pre><p>The above command will generate the <code>DatabaseModule</code> and include it in your <code>app.module.ts</code> file. You may, then, open the generated module file at <code>src/database/database.module.ts</code> and add the following database configuration to it:</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">Module</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ConfigModule</span>, <span class="hljs-title">ConfigService</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/config'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">TypeOrmModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/typeorm'</span>;

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> ({
        <span class="hljs-keyword">type</span>: <span class="hljs-string">'postgres'</span>,
        host: configService.get(<span class="hljs-string">'DB_HOST'</span>),
        port: configService.get(<span class="hljs-string">'DB_PORT'</span>),
        username: configService.get(<span class="hljs-string">'DB_USERNAME'</span>),
        password: configService.get(<span class="hljs-string">'DB_PASSWORD'</span>),
        database: configService.get(<span class="hljs-string">'DB_DATABASE'</span>),
        autoLoadEntities: <span class="hljs-literal">true</span>,
        synchronize: <span class="hljs-literal">false</span>,
        ...(configService.get(<span class="hljs-string">'DB_SSL'</span>) ? {
          ssl: <span class="hljs-literal">true</span>,
          extra: {
            ssl: {
              rejectUnauthorized: <span class="hljs-literal">false</span>
            }
          },
        } : {})
      }),
    }),
  ],
})
export class DatabaseModule {}
</code></pre><p>You may now set up the configuration module, such that it loads the content in the <code>.env file</code>, merges it with the externally defined environment variables, validates, and avail it through the configuration service by pasting the following code in your <code>app.module.ts</code>:</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">Module</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">AppController</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./app.controller'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">AppService</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./app.service'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ConfigModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/config'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">DatabaseModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./database/database.module'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">Joi</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'joi'</span>;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: <span class="hljs-literal">true</span>,
      envFilePath: <span class="hljs-string">'.env'</span>,
      validationSchema: Joi.object({
        PORT: Joi.number().default(<span class="hljs-number">4000</span>),
        NODE_ENV: Joi.string().default(<span class="hljs-string">'development'</span>),
        DB_HOST: Joi.string().required(),
        DB_PORT: Joi.number().required(),
        DB_USERNAME: Joi.string().required(),
        DB_PASSWORD: Joi.string().required(),
        DB_DATABASE: Joi.string().required(),
        DB_SSL: Joi.boolean().required(),
      }),
    }),
    DatabaseModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
</code></pre><p>At this point, the nest app should be able to connect to the database.</p>
<p><strong>Constants &amp; Exceptions</strong></p>
<p>Up to now, we’ve been focusing on environment variables but the API will also need constants such as the app’s name, set up somewhere in the app.</p>
<p>For this, create a folder named <code>common</code> in your <code>src</code> folder and place two files in it: <code>constants.ts</code> and <code>exceptions.ts</code> .</p>
<p>Place the following in your constants.ts file:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> APP_NAME = <span class="hljs-string">"Accounting App REST API"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> APP_DESCRIPTION = <span class="hljs-string">"Simple accounting API."</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> APP_VERSION = <span class="hljs-string">"1.0.0"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> PASSWORD_HASH_SALT: <span class="hljs-built_in">number</span> = <span class="hljs-number">10</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> JWT_EXPIRES_IN = <span class="hljs-string">'7d'</span>;
</code></pre><p>And the following in your exceptions.ts file:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> E_USER_EMAIL_TAKEN = <span class="hljs-string">"Oops! This email is already taken!"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> E_USER_NOT_FOUND = <span class="hljs-string">"User not found!"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> E_INCORRECT_EMAIL_OR_PASSWORD = <span class="hljs-string">"Email or password entered is incorrect!"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> E_PASSWORD_INCORRECT = <span class="hljs-string">"The password entered is incorrect!"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> E_TOO_MANY_REQUESTS = <span class="hljs-string">"Too many requests!"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> E_TRANSACTION_NOT_FOUND = <span class="hljs-string">"User not found! Please make sure the transaction ID entered is valid!"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> E_UNAUTHORIZED_ACCESS_TO_RESOURCE = <span class="hljs-string">"You are now allowed to access this resource!"</span>;
</code></pre><h3 id="heading-step-3-migrations-setup">Step 3: Migrations Setup</h3>
<p>Migrations are files that contain SQL queries for updating a database schema or applying new changes to an existing database.</p>
<p>TypeORM allows you to synchronize the database schema with the entities by setting <code>synchronize: true</code> in the database module configuration object. This is not recommended in production though, because they might cause unwanted data loss or transformation; Which is why, it’s better to set up migrations.</p>
<p>For this, we’re going to create a new <strong>TypeORM</strong> database configuration file <code>datasource.ts</code> which we shall place inside the <code>src/database</code> and we shall later reference it in our <code>package.json</code> scripts so that the TypeORM CLI, which we’ll use to run migrations, can access our database. After creating the <code>datasource.ts</code> file, paste the following code in it:</p>
<p>Then, add the following scripts to your project’s <code>package.json</code>:</p>
<pre><code>"migration:<span class="hljs-keyword">create</span><span class="hljs-string">": "</span>ts-node ./node_modules/typeorm/cli.js <span class="hljs-keyword">migration</span>:<span class="hljs-keyword">create</span><span class="hljs-string">",
"</span><span class="hljs-keyword">migration</span>:generate<span class="hljs-string">": "</span>ts-node ./node_modules/typeorm/cli.js <span class="hljs-keyword">migration</span>:generate <span class="hljs-comment">--dataSource src/database/datasource.ts",</span>
<span class="hljs-string">"migration:run"</span>: <span class="hljs-string">"ts-node ./node_modules/typeorm/cli.js migration:run -d src/database/datasource.ts"</span>,
<span class="hljs-string">"migration:revert"</span>: <span class="hljs-string">"ts-node ./node_modules/typeorm/cli.js migration:revert -d src/database/datasource.ts"</span>,
</code></pre><p>For the sake of simplicity, just make sure you do the following regarding migrations:</p>
<ul>
<li><strong>Generate a migration whenever there’s a change in your entities</strong>: Run: <code>npm run migration:generate src/database/migrations/NameOfTheChange</code> . Make sure you replace <code>NameOfTheChange</code> with a name referring to the change you’ve actually made in your entities. <em>Eg: CreateUsersTable</em>.</li>
<li><strong>Run migrations after every migration change</strong>. Make sure you’ve done <code>npm run build</code> before running migrations. Use the following command to run migrations: <code>npm run migration:run</code></li>
</ul>
<p>The above commands just allow you to work faster but you can always manually write your own migrations. To do so, first create an empty migration by running <code>npm run migration:create src/database/migrations/NameOfTheChange</code> , then open the migration generated at <code>src/database/migrations/</code>.</p>
<p>Inside every migration file there are two functions:</p>
<ul>
<li><strong>Up</strong>: This is where you write the SQL code that applies the change in your database schema.</li>
<li><strong>Down</strong>: Here you write the SQL code that reverses whatever is done in the up function.
To learn more about migrations, click <a target="_blank" href="https://typeorm.io/migrations">here</a>.</li>
</ul>
<h3 id="heading-step-4-basic-security-setup">Step 4. Basic Security Setup</h3>
<p>In order to mitigate or protect our API against certain security exploits, we’re going to implement helmet package, enable CORS, and rate limiting.</p>
<p>For this, copy the code below in your <code>main.ts</code> file.</p>
<pre><code><span class="hljs-keyword">import</span> { <span class="hljs-title">NestFactory</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/core'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">AppModule</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./app.module'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">helmet</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'helmet'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">rateLimit</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'express-rate-limit'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dotenv/config'</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> { <span class="hljs-title">ValidationPipe</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">E_TOO_MANY_REQUESTS</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./common/exceptions'</span>;

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bootstrap</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// -- App Instantiation</span>
  const app <span class="hljs-operator">=</span> await NestFactory.create(AppModule);

  <span class="hljs-comment">// -- Helmet</span>
  app.use(helmet());

  <span class="hljs-comment">// -- Cors setup</span>
  app.enableCors({
    origin: <span class="hljs-literal">false</span>, <span class="hljs-comment">// Specify the allowed origins.  I'm setting false to allow requests from any origin</span>
    <span class="hljs-comment">// Find more configuration options here: https://github.com/expressjs/cors#configuration-options</span>
  });

  <span class="hljs-comment">// -- Rate limiting: Limits the number of requests from the same IP in a period of time.</span>
  <span class="hljs-comment">// -- More at: https://www.npmjs.com/package/express-rate-limit</span>
  app.use(rateLimit({
    windowMs: <span class="hljs-number">10</span> <span class="hljs-operator">*</span> <span class="hljs-number">60</span> <span class="hljs-operator">*</span> <span class="hljs-number">1000</span>, <span class="hljs-comment">// 10 minutes</span>
    max: <span class="hljs-number">100</span>, <span class="hljs-comment">// Limit each IP to 100 requests per `window` (here, per 10 minutes)</span>
    standardHeaders: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Return rate limit info in the `RateLimit-*` headers</span>
    legacyHeaders: <span class="hljs-literal">false</span>, <span class="hljs-comment">// Disable the `X-RateLimit-*` headers,</span>
    skipSuccessfulRequests: <span class="hljs-literal">false</span>, <span class="hljs-comment">// The counting will skip all successful requests and just count the errors. Instead of removing rate-limiting, it's better to set this to true to limit the number of times a request fails. Can help prevent against brute-force attacks</span>
    message: { <span class="hljs-string">"message"</span>: E_TOO_MANY_REQUESTS, <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">403</span>, }
  }));

  <span class="hljs-comment">// -- Validation</span>
  app.useGlobalPipes(<span class="hljs-keyword">new</span> ValidationPipe({
    whitelist: <span class="hljs-literal">true</span>,
    transform: <span class="hljs-literal">true</span>,
    transformOptions: {
      enableImplicitConversion: <span class="hljs-literal">true</span>,
    },
  }));

  <span class="hljs-comment">// -- Start listening</span>
  await app.listen(process.env.PORT ? parseInt(process.env.PORT) : <span class="hljs-number">3000</span>);
}
bootstrap();
</code></pre><blockquote>
<p><strong>Note</strong>: Nest has its own rate limiter called <a target="_blank" href="https://github.com/nestjs/throttler">@nestjs/throttler</a> which is actually richer and easier to use. I chose the express-rate-limit package because <a target="_blank" href="https://github.com/nestjs/throttler">@nestjs/throttler</a> seems, at the time of writing this article, to be having dependency conflicts with the latest Nest version.</p>
</blockquote>
<h3 id="heading-step-5-swagger-setup">Step 5. Swagger Setup</h3>
<p>Nest provides the module <a target="_blank" href="https://docs.nestjs.com/openapi/introduction">@nestjs/swagger</a>, which allows generating the <a target="_blank" href="https://oai.github.io/Documentation/introduction.html">OpenAPI specification</a> using decorators, and thus, automatically generating the documentation for our REST API.</p>
<p>To get started, add the following code to your <code>main.ts</code> file’s bootstrap function before <code>app.listen()</code>.</p>
<pre><code>const config <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> DocumentBuilder()
  .setTitle(APP_NAME)
  .setDescription(APP_DESCRIPTION)
  .setVersion(APP_VERSION)
  .addBearerAuth() <span class="hljs-comment">// The API will use Bearer Authentication</span>
  .addBasicAuth({ <span class="hljs-keyword">type</span>: <span class="hljs-string">'apiKey'</span>, name: <span class="hljs-string">'accessToken'</span>, in: <span class="hljs-string">'query'</span> }) <span class="hljs-comment">// The API will use basic authentication for admin access</span>
  .build();
const document <span class="hljs-operator">=</span> SwaggerModule.createDocument(app, config);
SwaggerModule.setup(<span class="hljs-string">'docs'</span>, app, document);
</code></pre><p>Our API’s documentation will, now, be accessible at the <code>/docs</code> path.</p>
<p>The <code>APP_NAME</code>, <code>APP_DESCRIPTION</code> and <code>APP_VERSION</code> in the code above, are imported from the <code>src/common/constants.ts</code> file that we set up earlier and the <code>DocumentBuilder</code> and <code>SwaggerModule</code> objects are imported from the <a target="_blank" href="https://docs.nestjs.com/openapi/introduction">@nestjs/swagger</a> module.</p>
<p>From here we shall be using decorators on controllers, their methods, and data transfer objects (DTO) to enrich the API’s documentation.</p>
<p>You may read more about implementing the <strong>OpenAPI specification(OAS)</strong> to a <strong>NestJS</strong> app <a target="_blank" href="https://docs.nestjs.com/openapi/introduction">here</a>.</p>
<h3 id="heading-step-6-implementing-the-modules">Step 6. Implementing the modules</h3>
<p>An important way to protect our API is to restrict access to certain routes:</p>
<ul>
<li>Authentication routes should be accessible to anyone</li>
<li>Account and Transactions’ routes should be accessible only to authenticated users (users with a valid access token).</li>
<li>Users’ routes (fetching users) should only be accessible to owners of the API (people having an admin access token set in the environment variables).</li>
</ul>
<p>####Users &amp; Authentication</p>
<p>To get started, generate our needed modules by running <code>nest g resource users</code> then <code>nest g module auth</code>. You’ll be prompted with two questions when generating the users' module, respond with “REST API” to the first and with “Y” to the second.</p>
<p>We won’t be able to go through every step in detail, check <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api">this GitHub repository</a> for the code reference and follow the steps below:</p>
<ul>
<li>Add the files inside <code>src/common/dto and src/common/models</code> to your project. These files are used in more than one place, they are in the <code>common</code> folder to avoid pointless code repetition.</li>
<li>Add properties to the <code>user.entity.ts</code> file as it’s done <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api/blob/main/src/users/entities/user.entity.ts">here</a>.</li>
<li>Create a <code>user.model.ts</code> file in the <code>src/users/models</code> directory and add the user model properties to it; Do the same for the <code>users.model.ts</code> file. These are the files that describe request responses, mostly for API documentation; Which is why there are many decorators describing each property.</li>
<li>Add the user module’s DTOs as it’s done on the GitHub repository. These files describe the schema of request parameters regarding the user module for the API documentation.</li>
<li>Complete the <code>users.service.ts</code> file then the <code>users.controller.ts</code> file by implementing their methods.</li>
<li>Create the <code>account.controller.ts</code> file in the <code>users</code> module to hold the methods for user account management: profile update, etc. Ignore the account balance method at this stage.</li>
<li>Add the <code>account.controller.ts</code>, the <code>users.controller.ts</code> and the <code>users.service.ts</code> to the <code>users.module.ts</code> file. Make sure this module exports <code>TypeOrmModule</code> and <code>UsersService</code> and imports <code>TypeOrmModule.forFeature([User])</code> only at this stage. The exports ensure that whichever module that imports the current module can benefit from its exported providers.</li>
<li>Go to the <code>auth</code> folder and add the <code>login.dto.ts</code> file and <code>connection.model.ts</code> file.</li>
<li>Complete the <code>auth.service.ts</code> file, the <code>auth.controller.ts</code> and the <code>auth.module.ts</code> file as done here.</li>
<li>Add <code>jwt.strategy.ts</code> then the <code>user-auth.guard.ts</code> file, the <code>admin-auth.guard.ts</code> file, and the <code>current-user.decorator.ts</code> file.</li>
</ul>
<p>At this stage, the API’s authentication is properly set up; we can now add other modules.</p>
<p>####Other modules</p>
<p>This is where you add all the other modules based on the data model of the API you’re building. For our simple accounting app, we’ll just add one module: The transactions module.</p>
<p>First, run <code>nest g resource transactions</code>, then go inside the generated transactions module and follow the steps below:</p>
<ul>
<li>Create a folder named <code>enums</code> in the transactions folder and place both the <code>transaction-type.enum.ts</code> and the <code>transaction-category.enum.ts</code> files inside.</li>
<li>Complete the transactions’ entity file with its properties as done <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api/blob/main/src/transactions/entities/transaction.entity.ts">here</a>.</li>
<li>Create a <code>transaction.model.ts</code> and <code>transactions.model.ts</code> files in the <code>src/transactions/models</code> directory and add model fields to it.</li>
<li>Add the transaction module’s DTOs as it’s done <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api/tree/main/src/transactions/dto">here</a>.</li>
<li>Complete the <code>transactions.service.ts</code> file then the <code>transactions.controller.ts</code> file by implementing their methods.</li>
<li>Add the <code>transactions.controller.ts</code> and the <code>transactions.service.ts</code> to the <code>transactions.module.ts</code> file. Make sure this module exports <code>TypeOrmModule</code> and <code>TransactionsService</code> and imports <code>TypeOrmModule.forFeature([Transaction])</code> .</li>
<li>Next, head over to the users' module and add the <code>TransactionsModule</code> to the imports array of the <code>UsersModule</code> so that, from the <code>AccountController</code> we can access the <code>TransactionsService</code> from the <code>TransactionsModule</code> which contains the method we need for calculating the user account balance.</li>
<li>Implement the <code>getAccountBalance</code> method in the <code>account.controller.ts</code> file as done <a target="_blank" href="https://github.com/chrisbalola/accounting-app-api/blob/main/src/users/account.controller.ts">here</a>.
These steps would apply to almost any other module you’d add as long as you understand how all these files fit together.</li>
</ul>
<blockquote>
<p>Note: Feel free to change the architecture of the project; just make sure you follow the basic rules of writing clean code.</p>
</blockquote>
<h2 id="heading-deployment">Deployment</h2>
<p>There are many different ways to deploy APIs and many platforms to deploy them to. For this tutorial, I chose to show you how to deploy to <strong>Heroku</strong> because it has a free option and it’s very simple to get started with. I plan to write another article that will cover the deployment of REST APIs on the most popular platforms like <strong>AWS</strong>.</p>
<p>Let’s make our deployment setup and make sure it’s linked to GitHub:</p>
<p>First add the following to your <code>package.json</code> scripts: <code>"preinstall": "rm -rf /dist"</code> to the <code>dist</code> folder before installing dependencies and <code>"postinstall": "npm run build &amp;&amp; npm run migration:run"` to build the app and run migrations after dependencies are installed and before starting the app.
At the root of your project, create a file of the name:</code>Procfile<code>(no extension). Then paste the following inside:</code>web: npm run start:prod<code>. This will tell **Heroku** to start our app once it’s deployed.
Create a folder named</code>.github<code>at the root folder of your project, create another folder named</code>workflows<code>inside the</code>.github<code>folder, then add a file of the name</code>main.yml<code>inside the</code>workflows<code>folder.
Paste the following in the</code>main.yml``` file:</p>
<pre><code><span class="hljs-attribute">name</span>: Deploy

<span class="yaml"><span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">akhileshns/heroku-deploy@v3.12.12</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">heroku_api_key:</span> <span class="hljs-string">${{secrets.HEROKU_API_KEY}}</span>
          <span class="hljs-attr">heroku_app_name:</span> <span class="hljs-string">"cb-accounting-app-api"</span> 
          <span class="hljs-attr">heroku_email:</span> <span class="hljs-string">${{secrets.HEROKU_USER_EMAIL}}</span></span>
</code></pre><p>This is a GitHub workflow, based on which, GitHub will deploy the app to Heroku every time there’s a change on the main branch of our API’s repository.</p>
<p>Do not forget to build and generate migrations before deploying the API. You need to build before because we are generating based on the JS versions of our API’s entities, available inside the <code>/dist</code> folder.</p>
<p>Before actually pushing the project to your GitHub repository with these settings, first make sure you have created a Heroku app to deploy to and have added its name and your Heroku API Key to your repository’s GitHub secrets.</p>
<p><strong>Heroku Setup</strong></p>
<p>Please feel free to skip this step if you know how to create a Heroku app.</p>
<ul>
<li>Log into your Heroku account (Or create an account)</li>
<li>In the dashboard, click on “New” then on “Create a new app”.</li>
<li>Under the app’s page; click on the “Resources” tab menu and in the addons search box, type “Heroku Postgres” and select it. This will open a dialog for adding Postgres to your app, then click on “Submit Order Form”</li>
<li>Click on the “Settings” tab then on “Reveal Config Vars” and add your project’s environment variables there.<blockquote>
<p>You may find your Heroku Database credentials by going to Heroku Data, clicking on the database assigned to your application then selecting the “Settings” tab.</p>
</blockquote>
</li>
</ul>
<p>Adding the GitHub secrets</p>
<ul>
<li>Go to your project repository’s settings on GitHub.</li>
<li>Click on “Secrets” on the left menu, then on “Actions” in the drop-down menu. You may add the repository secrets that are needed by the <code>mail.yml</code> file there.<blockquote>
<p>Your Heroku API key can be found in your Heroku user account settings.</p>
</blockquote>
</li>
</ul>
<p>Looks like we’re all set! You may now push your app to the GitHub repository and have it deployed to Heroku automatically.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Using <strong>NestJS</strong> can significantly help you structure your <strong>NodeJS</strong> API project better but it takes more than just using good libraries and frameworks to build a <em>quality and secure API</em>.</p>
<p>Thanks for following this tutorial, if you found it helpful please feel free to like or comment. All your suggestions or comments are welcome. You can also connect with me on <a target="_blank" href="https://twitter.com/chrisbalola">Twitter</a>, <a target="_blank" href="https://www.linkedin.com/in/chrisbalola/">LinkedIn</a> or <a target="_blank" href="https://github.com/chrisbalola">GitHub</a>.</p>
<p>Cheers!</p>
]]></content:encoded></item></channel></rss>