- 1. API with NestJS #1. Controllers, routing and the module structure
- 2. API with NestJS #2. Setting up a PostgreSQL database with TypeORM
- 3. API with NestJS #3. Authenticating users with bcrypt, Passport, JWT, and cookies
- 4. API with NestJS #4. Error handling and data validation
- 5. API with NestJS #5. Serializing the response with interceptors
- 6. API with NestJS #6. Looking into dependency injection and modules
- 7. API with NestJS #7. Creating relationships with Postgres and TypeORM
- 8. API with NestJS #8. Writing unit tests
- 9. API with NestJS #9. Testing services and controllers with integration tests
- 10. API with NestJS #10. Uploading public files to Amazon S3
- 11. API with NestJS #11. Managing private files with Amazon S3
- 12. API with NestJS #12. Introduction to Elasticsearch
- 13. API with NestJS #13. Implementing refresh tokens using JWT
- 14. API with NestJS #14. Improving performance of our Postgres database with indexes
- 15. API with NestJS #15. Defining transactions with PostgreSQL and TypeORM
- 16. API with NestJS #16. Using the array data type with PostgreSQL and TypeORM
- 17. API with NestJS #17. Offset and keyset pagination with PostgreSQL and TypeORM
- 18. API with NestJS #18. Exploring the idea of microservices
- 19. API with NestJS #19. Using RabbitMQ to communicate with microservices
- 20. API with NestJS #20. Communicating with microservices using the gRPC framework
- 21. API with NestJS #21. An introduction to CQRS
- 22. API with NestJS #22. Storing JSON with PostgreSQL and TypeORM
- 23. API with NestJS #23. Implementing in-memory cache to increase the performance
- 24. API with NestJS #24. Cache with Redis. Running the app in a Node.js cluster
- 25. API with NestJS #25. Sending scheduled emails with cron and Nodemailer
- 26. API with NestJS #26. Real-time chat with WebSockets
- 27. API with NestJS #27. Introduction to GraphQL. Queries, mutations, and authentication
- 28. API with NestJS #28. Dealing in the N + 1 problem in GraphQL
- 29. API with NestJS #29. Real-time updates with GraphQL subscriptions
- 30. API with NestJS #30. Scalar types in GraphQL
- 31. API with NestJS #31. Two-factor authentication
- 32. API with NestJS #32. Introduction to Prisma with PostgreSQL
- 33. API with NestJS #33. Managing PostgreSQL relationships with Prisma
- 34. API with NestJS #34. Handling CPU-intensive tasks with queues
- 35. API with NestJS #35. Using server-side sessions instead of JSON Web Tokens
- 36. API with NestJS #36. Introduction to Stripe with React
- 37. API with NestJS #37. Using Stripe to save credit cards for future use
- 38. API with NestJS #38. Setting up recurring payments via subscriptions with Stripe
- 39. API with NestJS #39. Reacting to Stripe events with webhooks
- 40. API with NestJS #40. Confirming the email address
- 41. API with NestJS #41. Verifying phone numbers and sending SMS messages with Twilio
- 42. API with NestJS #42. Authenticating users with Google
- 43. API with NestJS #43. Introduction to MongoDB
- 44. API with NestJS #44. Implementing relationships with MongoDB
- 45. API with NestJS #45. Virtual properties with MongoDB and Mongoose
- 46. API with NestJS #46. Managing transactions with MongoDB and Mongoose
- 47. API with NestJS #47. Implementing pagination with MongoDB and Mongoose
- 48. API with NestJS #48. Definining indexes with MongoDB and Mongoose
- 49. API with NestJS #49. Updating with PUT and PATCH with MongoDB and Mongoose
- 50. API with NestJS #50. Introduction to logging with the built-in logger and TypeORM
- 51. API with NestJS #51. Health checks with Terminus and Datadog
- 52. API with NestJS #52. Generating documentation with Compodoc and JSDoc
- 53. API with NestJS #53. Implementing soft deletes with PostgreSQL and TypeORM
- 54. API with NestJS #54. Storing files inside a PostgreSQL database
- 55. API with NestJS #55. Uploading files to the server
- 56. API with NestJS #56. Authorization with roles and claims
- 57. API with NestJS #57. Composing classes with the mixin pattern
- 58. API with NestJS #58. Using ETag to implement cache and save bandwidth
- 59. API with NestJS #59. Introduction to a monorepo with Lerna and Yarn workspaces
- 60. API with NestJS #60. The OpenAPI specification and Swagger
- 61. API with NestJS #61. Dealing with circular dependencies
- 62. API with NestJS #62. Introduction to MikroORM with PostgreSQL
- 63. API with NestJS #63. Relationships with PostgreSQL and MikroORM
- 64. API with NestJS #64. Transactions with PostgreSQL and MikroORM
- 65. API with NestJS #65. Implementing soft deletes using MikroORM and filters
- 66. API with NestJS #66. Improving PostgreSQL performance with indexes using MikroORM
- 67. API with NestJS #67. Migrating to TypeORM 0.3
- 68. API with NestJS #68. Interacting with the application through REPL
- 69. API with NestJS #69. Database migrations with TypeORM
- 70. API with NestJS #70. Defining dynamic modules
- 71. API with NestJS #71. Introduction to feature flags
- 72. API with NestJS #72. Working with PostgreSQL using raw SQL queries
- 73. API with NestJS #73. One-to-one relationships with raw SQL queries
- 74. API with NestJS #74. Designing many-to-one relationships using raw SQL queries
- 75. API with NestJS #75. Many-to-many relationships using raw SQL queries
- 76. API with NestJS #76. Working with transactions using raw SQL queries
- 77. API with NestJS #77. Offset and keyset pagination with raw SQL queries
- 78. API with NestJS #78. Generating statistics using aggregate functions in raw SQL
- 79. API with NestJS #79. Implementing searching with pattern matching and raw SQL
- 80. API with NestJS #80. Updating entities with PUT and PATCH using raw SQL queries
- 81. API with NestJS #81. Soft deletes with raw SQL queries
- 82. API with NestJS #82. Introduction to indexes with raw SQL queries
- 83. API with NestJS #83. Text search with tsvector and raw SQL
- 84. API with NestJS #84. Implementing filtering using subqueries with raw SQL
- 85. API with NestJS #85. Defining constraints with raw SQL
- 86. API with NestJS #86. Logging with the built-in logger when using raw SQL
- 87. API with NestJS #87. Writing unit tests in a project with raw SQL
- 88. API with NestJS #88. Testing a project with raw SQL using integration tests
- 89. API with NestJS #89. Replacing Express with Fastify
- 90. API with NestJS #90. Using various types of SQL joins
- 91. API with NestJS #91. Dockerizing a NestJS API with Docker Compose
- 92. API with NestJS #92. Increasing the developer experience with Docker Compose
- 93. API with NestJS #93. Deploying a NestJS app with Amazon ECS and RDS
- 94. API with NestJS #94. Deploying multiple instances on AWS with a load balancer
- 95. API with NestJS #95. CI/CD with Amazon ECS and GitHub Actions
- 96. API with NestJS #96. Running unit tests with CI/CD and GitHub Actions
- 97. API with NestJS #97. Introduction to managing logs with Amazon CloudWatch
- 98. API with NestJS #98. Health checks with Terminus and Amazon ECS
- 99. API with NestJS #99. Scaling the number of application instances with Amazon ECS
- 100. API with NestJS #100. The HTTPS protocol with Route 53 and AWS Certificate Manager
- 101. API with NestJS #101. Managing sensitive data using the AWS Secrets Manager
- 102. API with NestJS #102. Writing unit tests with Prisma
- 103. API with NestJS #103. Integration tests with Prisma
- 104. API with NestJS #104. Writing transactions with Prisma
- 105. API with NestJS #105. Implementing soft deletes with Prisma and middleware
- 106. API with NestJS #106. Improving performance through indexes with Prisma
- 107. API with NestJS #107. Offset and keyset pagination with Prisma
- 108. API with NestJS #108. Date and time with Prisma and PostgreSQL
- 109. API with NestJS #109. Arrays with PostgreSQL and Prisma
- 110. API with NestJS #110. Managing JSON data with PostgreSQL and Prisma
- 111. API with NestJS #111. Constraints with PostgreSQL and Prisma
- 112. API with NestJS #112. Serializing the response with Prisma
- 113. API with NestJS #113. Logging with Prisma
- 114. API with NestJS #114. Modifying data using PUT and PATCH methods with Prisma
- 115. API with NestJS #115. Database migrations with Prisma
- 116. API with NestJS #116. REST API versioning
- 117. API with NestJS #117. CORS – Cross-Origin Resource Sharing
- 118. API with NestJS #118. Uploading and streaming videos
- 119. API with NestJS #119. Type-safe SQL queries with Kysely and PostgreSQL
- 120. API with NestJS #120. One-to-one relationships with the Kysely query builder
- 121. API with NestJS #121. Many-to-one relationships with PostgreSQL and Kysely
We should closely monitor the state of tests in our NestJS application. In a few previous parts of this series, we’ve used Docker and GitHub actions to design a CI/CD pipeline that automates our deployments. In this article, we avoid deploying faulty code by running unit tests before each pull request automatically.
Preparing the Docker image
The first step is ensuring that our tests can run in our Docker image. To verify that, let’s look at the Dockerfile we’ve built so far.
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# Installing dependencies: FROM node:18-alpine AS install-dependencies WORKDIR /user/src/app COPY package.json package-lock.json ./ RUN npm ci --omit=dev COPY . . # Creating a build: FROM node:18-alpine AS create-build WORKDIR /user/src/app COPY --from=install-dependencies /user/src/app ./ RUN npm run build USER node # Running the application: FROM node:18-alpine AS run WORKDIR /user/src/app COPY --from=install-dependencies /user/src/app/node_modules ./node_modules COPY --from=create-build /user/src/app/dist ./dist COPY package.json ./ CMD ["npm", "run", "start:prod"] |
If you want to see how we’ve built the above configuration, check out the following articles:
We would fall into a significant pitfall if we used the above Docker image to run tests.
To install the dependencies, we run npm ci --omit=dev. Adding the --omit=dev flag tells NPM to skip installing all of the libraries listed in devDependencies. However, this is typically where we list libraries such as Jest that we need for testing.
package.json
1 2 3 4 5 6 7 8 |
"devDependencies": { "jest": "^29.3.1", "eslint": "^8.31.0", "eslint-config-prettier": "^8.6.0", "eslint-plugin-import": "^2.27.4", "prettier": "^2.8.3" // ... } |
Let’s modify our configuration to accommodate that.
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# Installing dependencies: FROM node:18-alpine AS install-dependencies WORKDIR /user/src/app RUN npm install -g npm@9.5.0 COPY package.json package-lock.json ./ RUN npm ci COPY . . # Creating a build: FROM node:18-alpine AS create-build WORKDIR /user/src/app RUN npm install -g npm@9.5.0 COPY --from=install-dependencies /user/src/app ./ RUN npm run build USER node # Running the application: FROM node:18-alpine AS run WORKDIR /user/src/app RUN npm install -g npm@9.5.0 COPY --from=install-dependencies /user/src/app/node_modules ./node_modules COPY --from=create-build /user/src/app/dist ./dist COPY package.json ./ RUN npm prune --production CMD ["npm", "run", "start:prod"] |
Since we need the packages listed in devDependencies to run our unit tests, we install all of them during the install-dependencies stage by skipping the --omit=dev flag.
This would make the resulting Docker image heavier if not for a modification to the run stage. By adding the npm prune --production command at the bottom, we eliminate the libraries from devDependencies before making the final Docker image.
Thanks to doing all of the above, the result of the create-build stage in the Dockerfile has all of the dependencies, but they are stripped when no longer necessary.
We also add RUN npm install -g npm@9.5.0 to ensure the latest version of NPM and avoid warnings in the terminal.
Running tests locally
We must run our tests with the Docker image targeting the create-build stage before removing the devDependevies. Therefore, let’s use the --target flag to build the image without running the last stage from our Dockerfile.
1 |
docker build -t nestjs-api:test --target=create-build . |
When we look at the create-build stage in our Dockerfile, we can see that it doesn’t have the CMD keyword. When running the image, we must provide Docker with the desired command. Let’s run npm run test inside the shell of a Docker container running with our image.
1 |
docker run nestjs-api:test sh -c 'npm run test' |
PASS src/authentication/authentication.service.test.ts
The AuthenticationService
when creating a cookie
✓ should return a string (17 ms)Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.662 s
Ran all test suites.
Running Prettier
When checking the formatting, we usually set up Prettier using the --write flag to overwrite our files.
1 2 3 4 |
"scripts": { "format": "prettier --write \"src/**/*.ts\"", // ... } |
Instead, we want to be notified when our code does not meet the formatting standards. To do that, we need the --list-different flag.
1 2 3 4 |
"scripts": { "format:verify": "prettier --list-different \"src/**/*.ts\"", // ... } |
If a particular file is not formatted correctly when running the above command, Prettier lists it. It also uses the 1 exit code to signify an error.
Let’s rebuild our Docker image to include the above command.
1 |
docker build -t nestjs-api:test --target=create-build . |
Now, we can run Prettier in our Docker container to verify our code.
1 |
docker run nestjs-api:test sh -c 'npm run format:verify' |
Running ESLint
The official NestJS starter repository comes with ESLint configured and ready to go. We can easily use it with our existing configuration.
1 |
docker run nestjs-api:test sh -c 'npm run lint' |
Running the tests with GitHub Actions
In the previous part of this series, we created a GitHub Actions workflow that runs every time we push our change to the master branch. Before that happens, we usually make a pull request to merge our changes. Let’s create a new GitHub Actions workflow that runs every time a PR is created.
.github/workflows/test.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
name: Test the application on: [pull_request] env: IMAGE_TAG: nestjs-api jobs: test: name: Run tests runs-on: ubuntu-latest steps: - name: Check out the repository uses: actions/checkout@v3 - name: Build the Docker image run: docker build -t $IMAGE_TAG --target=create-build . - name: Run Prettier run: docker run $IMAGE_TAG sh -c 'npm run format:verify' - name: Run ESLint run: docker run $IMAGE_TAG sh -c 'npm run lint' - name: Run tests run: docker run $IMAGE_TAG sh -c 'npm run test' |
Thanks to adding on: [pull_request] above, there is a set of steps happening above every time we create a pull request:
- For GitHub Actions to access our code, we need to check out our repository. To do that, we use the actions/checkout@v3 action provided by GitHub.
- We build the Docker image targeting the create-build stage.
- Since our environment is ready, we run Prettier, ESLint, and unit tests.
Let’s create a Pull Request and verify that everything works as expected.
We can see the information saying that “all checks have passed“. When we click “Details”, we can see more job information.
Protecting our master branch
Even though we have the tests in place, nothing stops us from disregarding them and merging the changes even if they fail. Let’s change that.
To do that, we need to open our GitHub repository and go to Settings -> Branches. Here, we can add a branch protection rule prohibiting merging changes if our status checks haven’t succeeded.
Summary
In this article, we established a process of testing our application whenever we want to merge new changes to the master branch. To do that, we had to modify our Docker image slightly to install all necessary dependencies and remove them when they were no longer needed. We’ve also created a new GitHub Actions workflow to run tests on every pull request.
Thanks to the above, we couldn’t merge our changes if any of our tests failed. This gives us an additional layer of security and prevents us from deploying a faulty version of our application.
You are amazing bro! Thanks for your awesome work.
Can you make a tutorial on how to export files with excel, csv, pdf and import data files? I have no idea to implement with Nestjs. Thanks Bro