Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,9 @@ tags:
<i>10 min read</i>
</center>

Welcome to the fifth and final tutorial in this series about building a REST API with NestJS and Prisma! In this tutorial, you will add JWT authentication to the API with Passport: a login endpoint that issues tokens, a JWT strategy and guard that protect routes, Swagger integration, and password hashing with bcrypt.
JWT authentication in a NestJS API takes three pieces: a login endpoint that signs tokens, a `passport-jwt` strategy that verifies them, and a guard that protects routes. This tutorial is the fifth and final part of a five-part series on building a REST API with NestJS and Prisma ORM. In it, you will protect the users endpoints with JWT authentication, integrate the auth flow with Swagger, and replace plain text passwords with bcrypt hashes.

> **Updated (July 2026):** This tutorial has been fully revised for **Prisma ORM 7** and **NestJS 11**, and continues from the updated [fourth part](/nestjs-prisma-relational-data-7D056s1kOabc) of the series. Every command and code block below was run end-to-end against `prisma@7.8`, `@prisma/client@7.8`, `@nestjs/core@11`, `@nestjs/passport@11` and `bcrypt@6`, with the full login flow verified against a live [Prisma Postgres](https://www.prisma.io/postgres) database.

## Table Of Contents

- [Introduction](#introduction)
- [Development environment](#development-environment)
- [Set up the project](#set-up-the-project)
- [Project structure and files](#project-structure-and-files)
- [Implement authentication in your REST API](#implement-authentication-in-your-rest-api)
- [Install and configure `passport`](#install-and-configure-passport)
- [Implement a `POST /auth/login` endpoint](#implement-a-post-authlogin-endpoint)
- [Implement JWT authentication strategy](#implement-jwt-authentication-strategy)
- [Implement JWT auth guard](#implement-jwt-auth-guard)
- [Integrate authentication in Swagger](#integrate-authentication-in-swagger)
- [Hashing passwords](#hashing-passwords)
- [Summary and final remarks](#summary-and-final-remarks)
> **Updated (July 2026):** This tutorial has been fully revised for **Prisma ORM 7** and **NestJS 11**, and continues from the updated [fourth part](/nestjs-prisma-relational-data-7D056s1kOabc) of the series. Every command and code block below was run end-to-end against `prisma@7.8`, `@prisma/client@7.8`, `@nestjs/core@11`, `@nestjs/passport@11` and `bcrypt@6`, with the full login flow verified against a live [Prisma Postgres](https://www.prisma.io/docs/postgres) database.

## Introduction

Expand All @@ -55,7 +40,7 @@ This tutorial continues from the end of the [fourth chapter](/nestjs-prisma-rela
To follow along with this tutorial, you will be expected to:

- ... have [Node.js](https://nodejs.org) (v18 or higher) installed.
- ... have a PostgreSQL database. The easiest option is [Prisma Postgres](https://www.prisma.io/postgres); you can also run Postgres locally with [Docker](https://www.docker.com/) or a native install.
- ... have a PostgreSQL database. The easiest option is Prisma Postgres; you can also run Postgres locally with [Docker](https://www.docker.com/) or a native install.
- ... have the [Prisma VSCode Extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) installed. _(optional)_
- ... have access to a Unix shell (like the terminal/shell in Linux and macOS) to run the commands provided in this series. _(optional)_

Expand Down Expand Up @@ -715,7 +700,7 @@ export class UsersService {

The `bcrypt.hash` function accepts two arguments: the input string to the hash function and the number of rounds of hashing (also known as cost factor). Increasing the rounds of hashing increases the time it takes to calculate the hash. There is a trade off here between security and performance. With more rounds of hashing, it takes more time to calculate the hash, which helps prevent brute force attacks. However, more rounds of hashing also mean more time to calculate the hash when a user logs in. [This Stack Exchange answer](https://security.stackexchange.com/a/17207) has a good discussion on this topic.

`bcrypt` also automatically uses another technique called [salting](<https://en.wikipedia.org/wiki/Salt_(cryptography)>) to make it harder to brute force the hash. Salting is a technique where a random string is added to the input string before hashing. This way, attackers cannot use a table of precomputed hashes to crack the password, as each password has a different salt value.
`bcrypt` also automatically uses another technique called salting to make it harder to brute force the hash. Salting is a technique where a random string is added to the input string before hashing. This way, attackers cannot use a table of precomputed hashes to crack the password, as each password has a different salt value.

You also need to update your database seed script to hash the passwords before inserting them into the database:

Expand Down Expand Up @@ -840,10 +825,24 @@ export class AuthService {
You can now login with the correct password and get a JWT in the response.


## Frequently asked questions

<Accordions type="single">
<Accordion title="How does the API verify a JWT on protected routes?">
The `passport-jwt` strategy extracts the bearer token from the `Authorization` header, verifies its signature against the configured secret, and passes the decoded payload to the `validate()` method, which loads the user from the database. Requests without a valid token get an HTTP 401 response from the `JwtAuthGuard`.
</Accordion>
<Accordion title="Why does login return 401 after enabling password hashing?">
Because the stored passwords are now bcrypt hashes, a plain equality check against the submitted password always fails. Compare with `bcrypt.compare(password, user.password)` instead, and reseed the database so the seed users' passwords are hashed too.
</Accordion>
<Accordion title="How long are the issued tokens valid?">
As long as the `expiresIn` value passed to `JwtModule.register`; this tutorial uses `'5m'`. After expiry, requests fail with HTTP 401 and the client needs to log in again for a new token.
</Accordion>
</Accordions>

## Summary and final remarks

In this chapter, you learned how to implement JWT authentication in your NestJS REST API. You also learned about salting passwords and integrating authentication with Swagger.

This wraps up the series. Across five chapters, you built a REST API with NestJS 11 and Prisma ORM 7, added input validation, error handling, relational data and authentication; every piece verified on the current stack. From here, good next steps are [The Ultimate Guide to Testing with Prisma](https://www.prisma.io/blog/series/testing-with-prisma) for test coverage, the [Prisma Migrate docs](https://www.prisma.io/docs/orm/prisma-migrate/getting-started) for evolving your schema, and [Prisma Postgres](https://www.prisma.io/postgres) as the database for your production deployment.
This wraps up the series. Across five chapters, you built a REST API with NestJS 11 and Prisma ORM 7, added input validation, error handling, relational data and authentication; every piece verified on the current stack. From here, good next steps are [The Ultimate Guide to Testing with Prisma](https://www.prisma.io/blog/series/testing-with-prisma) for test coverage, the [Prisma Migrate docs](https://www.prisma.io/docs/orm/prisma-migrate/getting-started) for evolving your schema, and Prisma Postgres as the database for your production deployment.

Looking ahead: [Prisma Next](https://www.prisma.io/docs/orm) is a TypeScript-native rewrite of Prisma ORM, built for AI coding agents and currently in early access. It becomes Prisma 8 at general availability; until then, Prisma 7 stays the production choice. To try it, run `npm create prisma@next` or read the [early access docs](https://pris.ly/pn-ea).
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,10 @@ tags:
<i>6 min read</i>
</center>

Welcome to the third tutorial in this series about building a REST API with NestJS and Prisma! In this tutorial, you will learn how to handle errors in a NestJS application, using two strategies: throwing exceptions directly in your route handlers, and building an exception filter that turns Prisma error codes into meaningful HTTP responses.
Error handling in NestJS happens at two levels: you throw HTTP exceptions like `NotFoundException` directly in your route handlers, and you catch everything else with exception filters. This tutorial is the third part of a five-part series on building a REST API with NestJS and Prisma ORM. In it, you will return a proper 404 for missing articles and build an exception filter that turns Prisma's `P2002` unique constraint error into a 409 Conflict response.

> **Updated (July 2026):** This tutorial has been fully revised for **Prisma ORM 7** and **NestJS 11**, and continues from the updated [first](/nestjs-prisma-rest-api-7D056s1BmOL0) and [second](/nestjs-prisma-validation-7D056s1kOla1) parts of the series. In Prisma 7 the `Prisma` namespace (including error classes like `PrismaClientKnownRequestError`) is imported from your generated Client instead of `@prisma/client`. Every command and code block below was run end-to-end against `prisma@7.8`, `@prisma/client@7.8` and `@nestjs/core@11`.

## Table Of Contents

- [Introduction](#introduction)
- [Development environment](#development-environment)
- [Set up the project](#set-up-the-project)
- [Project structure and files](#project-structure-and-files)
- [Detect and throw exceptions directly](#detect-and-throw-exceptions-directly)
- [Handle exceptions by using exception filters](#handle-exceptions-by-using-exception-filters)
- [Advantages of a dedicated exception layer](#advantages-of-a-dedicated-exception-layer)
- [NestJS global exception filter](#nestjs-global-exception-filter)
- [Create a manual exception filter](#create-a-manual-exception-filter)
- [Apply the exception filter to your application](#apply-the-exception-filter-to-your-application)
- [Bonus: Handle Prisma exceptions with the `nestjs-prisma` package](#bonus-handle-prisma-exceptions-with-the-nestjs-prisma-package)
- [Summary and final remarks](#summary-and-final-remarks)


## Introduction

In the [first chapter](/nestjs-prisma-rest-api-7D056s1BmOL0) of this series, you created a new NestJS project and integrated it with Prisma ORM, PostgreSQL and Swagger. Then, you built a rudimentary REST API for the backend of a blog application. In the [second chapter](/nestjs-prisma-validation-7D056s1kOla1) you learned how to do input validation and transformation.
Expand All @@ -56,7 +40,7 @@ This tutorial continues from the end of the [second chapter](/nestjs-prisma-vali
To follow along with this tutorial, you will be expected to:

- ... have [Node.js](https://nodejs.org) (v18 or higher) installed.
- ... have a PostgreSQL database. The easiest option is [Prisma Postgres](https://www.prisma.io/postgres); you can also run Postgres locally with [Docker](https://www.docker.com/) or a native install.
- ... have a PostgreSQL database. The easiest option is [Prisma Postgres](https://www.prisma.io/docs/postgres); you can also run Postgres locally with [Docker](https://www.docker.com/) or a native install.
- ... have the [Prisma VSCode Extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) installed. _(optional)_
- ... have access to a Unix shell (like the terminal/shell in Linux and macOS) to run the commands provided in this series. _(optional)_

Expand Down Expand Up @@ -398,12 +382,26 @@ Instructions on installing and using the package are available in the [`nestjs-p
You can learn how to use the package to handle Prisma exceptions in the [Exception Filter](https://nestjs-prisma.dev/docs/exception-filter/) section of the documentation.


## Frequently asked questions

<Accordions type="single">
<Accordion title="What happens to an unhandled Prisma error in NestJS?">
The built-in global exception filter catches it and returns a generic HTTP 500 "Internal server error" response, while the full error (including the Prisma error code, like `P2002`) is only visible in the server logs. A custom exception filter lets you map specific error codes to meaningful HTTP responses instead.
</Accordion>
<Accordion title="Where do I import PrismaClientKnownRequestError from in Prisma 7?">
From the `Prisma` namespace of your generated Client, for example `import { Prisma } from '../../generated/prisma/client'`. Importing it from `@prisma/client` no longer works with the `prisma-client` generator, which outputs the Client into your project.
</Accordion>
<Accordion title="Which Prisma error code signals a unique constraint violation?">
`P2002`. The exception filter in this tutorial maps it to an HTTP 409 Conflict response. A related code worth handling is `P2025` (record not found), which fits an HTTP 404 response for update and delete endpoints. The full list is in the [Prisma error reference](https://www.prisma.io/docs/orm/reference/error-reference).
</Accordion>
</Accordions>

## Summary and final remarks

Congratulations! You took an existing NestJS application in this tutorial and learned how to integrate error handling. You learned two different ways to handle errors: directly in your application code and by creating an exception filter.

In this chapter you learned how to handle Prisma errors, like the `P2002` unique constraint violation. But the techniques themselves are not limited to Prisma. You can use them to handle any type of error in your application.

In the [next part](/nestjs-prisma-relational-data-7D056s1kOabc) of this series, you will add a `User` model and learn how to handle relational data in your API. If you want to go deeper on the Prisma side first, the [Prisma getting started guide](https://www.prisma.io/docs/getting-started), [Prisma Migrate docs](https://www.prisma.io/docs/orm/prisma-migrate/getting-started), and [Prisma Postgres](https://www.prisma.io/postgres) are the best next resources for taking the app from tutorial to production-ready workflow.
In the [next part](/nestjs-prisma-relational-data-7D056s1kOabc) of this series, you will add a `User` model and learn how to handle relational data in your API. If you want to go deeper on the Prisma side first, the [Prisma getting started guide](https://www.prisma.io/docs/getting-started), [Prisma Migrate docs](https://www.prisma.io/docs/orm/prisma-migrate/getting-started), and Prisma Postgres are the best next resources for taking the app from tutorial to production-ready workflow.

Looking ahead: [Prisma Next](https://www.prisma.io/docs/orm) is a TypeScript-native rewrite of Prisma ORM, built for AI coding agents and currently in early access. It becomes Prisma 8 at general availability; until then, Prisma 7 stays the production choice. To try it, run `npm create prisma@next` or read the [early access docs](https://pris.ly/pn-ea).
Loading
Loading