# Building a REST API with NestJS and Prisma
URL: https://www.prisma.io/blog/nestjs-prisma-rest-api-7D056s1BmOL0
Published: 2022-06-03
Updated: 2026-07-08
Authors: Tasin Ishmam
Tags: education
Series: Building a REST API with NestJS, PostgreSQL, Swagger and Prisma
Description: Learn how to build a backend REST API with NestJS, Prisma ORM 7, PostgreSQL and Swagger. Set up the project, model your data, build the API and document it with Swagger. Fully updated for Prisma 7 and NestJS 11.
> Blog posts describe Prisma as it was on the date shown and are not kept current. Before implementing from a post, check the documentation index at https://www.prisma.io/docs/llms.txt and the changelog at https://www.prisma.io/changelog.md.
> For an index of every published post, fetch https://www.prisma.io/blog/llms.txt.
20 min read
NestJS is one of the most popular Node.js frameworks for building structured, type-safe server-side applications, and Prisma ORM gives it a type-safe database layer. This tutorial is the first part of a five-part series on building a REST API with NestJS and Prisma ORM. In it, you will generate a NestJS project, connect it to PostgreSQL with Prisma ORM 7, build CRUD endpoints for a blog application called "Median", and document the API with Swagger.
> **Updated (July 2026):** This tutorial has been fully revised for **Prisma ORM 7** and **NestJS 11**. Prisma 7 is Rust-free and uses [driver adapters](https://www.prisma.io/docs/orm/v7/core-concepts/supported-databases/database-drivers), generates the Prisma Client into your project (instead of `node_modules`), and is configured through a `prisma.config.ts` file. Every command and code block below was run end-to-end against `prisma@7.8`, `@prisma/client@7.8` and `@nestjs/core@11`. If you're starting fresh, pair Prisma ORM with [Prisma Postgres](https://www.prisma.io/docs/postgres) for a managed database that works out of the box.
## Introduction [#introduction]
In this tutorial, you will learn how to build the backend REST API for a blog application called "Median" (a simple [Medium](https://medium.com/) clone). You will get started by creating a new NestJS project. Then you will start a PostgreSQL database and connect to it using Prisma. Finally, you will build the REST API and document it with Swagger.

### Technologies you will use [#technologies-you-will-use]
You will be using the following tools to build this application:
* [NestJS](https://nestjs.com/) as the backend framework
* [Prisma ORM](https://www.prisma.io/) as the Object-Relational Mapper (ORM)
* [PostgreSQL](https://www.postgresql.org/) as the database (via Prisma Postgres or your own instance)
* [Swagger](https://swagger.io/) as the API documentation tool
* [TypeScript](https://www.typescriptlang.org/) as the programming language
## Prerequisites [#prerequisites]
### Assumed knowledge [#assumed-knowledge]
This is a beginner friendly tutorial. However, this tutorial assumes:
* Basic knowledge of JavaScript or TypeScript (preferred)
* Basic knowledge of NestJS
> **Note**: If you're not familiar with NestJS, you can quickly learn the basics by following the [overview section](https://docs.nestjs.com/first-steps) in the NestJS docs.
### Development environment [#development-environment]
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; 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)*
> **Note 1**: The optional Prisma VSCode extension adds IntelliSense and syntax highlighting for Prisma.
> **Note 2**: If you don't have a Unix shell (for example, you are on a Windows machine), you can still follow along, but the shell commands may need to be modified for your machine.
## Generate the NestJS Project [#generate-the-nestjs-project]
The first thing you will need is to install the NestJS CLI. The NestJS CLI comes in very handy when working with a NestJS project. It comes with built-in utilities that help you initialize, develop and maintain your NestJS application.
You can use the NestJS CLI to create an empty project. To start, run the following command in the location where you want the project to reside:
```shell
npx @nestjs/cli new median
```
The CLI will prompt you to choose a *package manager* for your project. Choose **npm**. Afterward, you should have a new NestJS project in the current directory.
Open the project in your preferred code editor (we recommend VSCode). You should see the following files:
```
median
├── node_modules
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── README.md
├── eslint.config.mjs
├── nest-cli.json
├── package-lock.json
├── package.json
├── tsconfig.build.json
└── tsconfig.json
```
Most of the code you work on will reside in the `src` directory. The NestJS CLI has already created a few files for you. Some of the notable ones are:
* `src/app.module.ts`: The root module of the application.
* `src/app.controller.ts`: A basic controller with a single route: `/`. This route will return a simple `'Hello World!'` message.
* `src/main.ts`: The entry point of the application. It will start the NestJS application.
You can start your project by using the following command:
```shell
npm run start:dev
```
This command will watch your files, automatically recompiling and reloading the server whenever you make a change. To verify the server is running, go to the URL [`http://localhost:3000/`](http://localhost:3000/). You should see an empty page with the message `'Hello World!'`.
> **Note**: You should keep the server running in the background as you go through this tutorial.
## Create a PostgreSQL instance [#create-a-postgresql-instance]
You will be using PostgreSQL as the database for your NestJS application. You have a couple of options for getting one.
**Option A: Prisma Postgres (recommended).** Create a free Prisma Postgres database in seconds, with no local setup, by running:
```shell
npx create-db
```
This prints a `DATABASE_URL` connection string (along with a link to claim the database so you can keep it). Copy the `DATABASE_URL`; you'll paste it into your `.env` file in the next section. You can also create a Prisma Postgres database from the [Prisma Console](https://console.prisma.io).
> **Note**: Prefer to run Postgres locally with no cloud at all? `npx prisma dev` starts a local Prisma Postgres server in your terminal. It works for this tutorial, but the local server doesn't support the `prisma migrate dev` workflow you'll use later, so run `npx prisma db push` instead of `migrate dev` if you go this route. For the smoothest path through every step below, use `npx create-db` (Option A) or Docker (Option B).
**Option B: Docker.** If you prefer to run Postgres yourself with Docker, create a `docker-compose.yml` file in the main folder of your project:
```shell
touch docker-compose.yml
```
Add the following configuration inside the file:
```yml
# docker-compose.yml
services:
postgres:
image: postgres:17
restart: always
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
volumes:
- postgres:/var/lib/postgresql/data
ports:
- '5432:5432'
volumes:
postgres:
```
A few things to understand about this configuration:
* The `image` option defines what Docker image to use. Here, you are using the [`postgres` image](https://hub.docker.com/_/postgres) version 17.
* The `environment` option specifies the environment variables passed to the container during initialization. You can define the configuration options and secrets (such as the username and password) the container will use here.
* The `volumes` option is used for persisting data in the host file system.
* The `ports` option maps ports from the host machine to the container. The format follows a `'host_port:container_port'` convention. In this case, you are mapping the port `5432` of the host machine to port `5432` of the `postgres` container. `5432` is conventionally the port used by PostgreSQL.
Make sure that nothing is running on port `5432` of your machine, then start the container:
```shell
docker compose up -d
```
Congratulations 🎉. You now have your own PostgreSQL database to play around with!
## Set up Prisma [#set-up-prisma]
Now that the database is ready, it's time to set up Prisma!
### Initialize Prisma [#initialize-prisma]
To get started, install the Prisma CLI along with two helpers you'll use later, `tsx` (to run the TypeScript seed script) and `dotenv` (to load environment variables), as development dependencies:
```shell
npm install -D prisma@7.9.1 tsx dotenv
```
You can initialize Prisma inside your project by running:
```shell
npx prisma init --datasource-provider postgresql
```
This creates a new `prisma` directory with a `schema.prisma` file, a **`prisma.config.ts`** file, and a `.env` file. In Prisma 7, `prisma.config.ts` is the central configuration file; it's where your database connection URL and other settings now live.
### Set your environment variable [#set-your-environment-variable]
Open the `.env` file and set `DATABASE_URL` to your database's connection string.
If you're using Prisma Postgres (Option A), paste the `DATABASE_URL` that `npx create-db` printed. It looks like this:
```shell
# .env
DATABASE_URL="postgres://:@db.prisma.io:5432/postgres?sslmode=require"
```
If you're using Docker (Option B), use the connection string for that instance:
```shell
# .env
DATABASE_URL="postgresql://myuser:mypassword@localhost:5432/median-db?schema=public"
```
> **Note**: The exact host, port and credentials depend on your setup. The connection string format for PostgreSQL is available in the [Prisma Docs](https://www.prisma.io/docs/orm/v7/core-concepts/supported-databases/postgresql#connection-url).
Now open `prisma.config.ts`. Prisma generated it to read `DATABASE_URL` from your environment via `dotenv`. It should look like this:
```ts
// prisma.config.ts
// This file was generated by Prisma, and assumes you have installed the following:
// npm install --save-dev prisma@7.9.1 dotenv
import "dotenv/config";
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: process.env["DATABASE_URL"],
},
});
```
### Understand the Prisma schema [#understand-the-prisma-schema]
If you open `prisma/schema.prisma`, you should see the following default schema:
```prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
}
```
This file is written in the *Prisma Schema Language*, which is a language that Prisma uses to define your database schema. The `schema.prisma` file has three main components:
* **Data source**: Specifies your database connection. The provider is set to `postgresql`. Notice the connection URL is no longer here; in Prisma 7 it lives in `prisma.config.ts`.
* **Generator**: Indicates that you want to generate Prisma Client, a type-safe query builder for your database. In Prisma 7, the `prisma-client` generator outputs the Client as source files into your project (here, `../generated/prisma`) rather than into `node_modules`. This makes the generated code transparent and bundler-friendly.
* **Data model**: Defines your database *models*. Each model will be mapped to a table in the underlying database. Right now there are no models in your schema; you will add one in the next section.
Because NestJS compiles to CommonJS, add one option to the generator so the generated Client matches that module format:
```prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client"
output = "../generated/prisma"
moduleFormat = "cjs"
}
datasource db {
provider = "postgresql"
}
```
> **Note**: Without `moduleFormat = "cjs"`, you may hit a runtime error like `ReferenceError: exports is not defined in ES module scope` when NestJS loads the generated Client. Setting it to `"cjs"` keeps the Client aligned with NestJS's build output. For more on the schema, check out the [Prisma docs](https://www.prisma.io/docs/orm/contract-authoring/psl-syntax).
### Model the data [#model-the-data]
Now it's time to define the data models for your application. For this tutorial, you will only need an `Article` model to represent each article on the blog.
Inside the `prisma/schema.prisma` file, add a new model to your schema named `Article`:
```prisma
// prisma/schema.prisma
model Article {
id Int @id @default(autoincrement())
title String @unique
description String?
body String
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
```
Here, you have created an `Article` model with several fields. Each field has a name (`id`, `title`, etc.), a type (`Int`, `String`, etc.), and other optional attributes (`@id`, `@unique`, etc.). Fields can be made optional by adding a `?` after the field type.
The `id` field has a special attribute called `@id`. This attribute indicates that this field is the primary key of the model. The `@default(autoincrement())` attribute indicates that this field should be automatically incremented and assigned to any newly created record.
The `published` field is a flag to indicate whether an article is published or in draft mode. The `@default(false)` attribute indicates that this field should be set to `false` by default.
The two `DateTime` fields, `createdAt` and `updatedAt`, will track when an article is created and when it was last updated. The `@updatedAt` attribute will automatically update the field with the current timestamp whenever an article is modified.
### Migrate the database [#migrate-the-database]
With the Prisma schema defined, you will run migrations to create the actual tables in the database. To generate and execute your first migration, run the following command in the terminal:
```shell
npx prisma migrate dev --name init
```
This command will do two things:
1. **Save the migration**: Prisma Migrate will take a snapshot of your schema and figure out the SQL commands necessary to carry out the migration. Prisma will save the migration file containing the SQL commands to the newly created `prisma/migrations` folder.
2. **Execute the migration**: Prisma Migrate will execute the SQL in the migration file to create the underlying tables in your database.
> **Note**: You can learn more about Prisma Migrate in the [Prisma docs](https://www.prisma.io/docs/orm/v7/prisma-migrate).
If completed successfully, you should see a message like this :
```
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20260625101323_init/
└─ migration.sql
Your database is now in sync with your schema.
```
After the migration completes, generate Prisma Client so it reflects your latest schema:
```shell
npx prisma generate
```
You should see output like this:
```
✔ Generated Prisma Client (7.8.0) to ./generated/prisma
```
> **Note**: In Prisma 7, `migrate dev` applies migrations but does not regenerate Prisma Client into your configured `output` folder. Run `npx prisma generate` after every schema change; without it, new models are missing from the Client at runtime.
> Check the generated migration file to get an idea about what Prisma Migrate is doing behind the scenes:
```sql
-- prisma/migrations/20260625101323_init/migration.sql
-- CreateTable
CREATE TABLE "Article" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"body" TEXT NOT NULL,
"published" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Article_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Article_title_key" ON "Article"("title");
```
> **Note**: The name of your migration file will be slightly different.
This is the SQL needed to create the `Article` table inside your PostgreSQL database. It was automatically generated and executed by Prisma based on your Prisma schema.
### Seed the database [#seed-the-database]
Currently, the database is empty. So you will create a *seed script* that will populate the database with some dummy data.
In Prisma 7, the Prisma Client is Rust-free and talks to your database through a **driver adapter**. You need two runtime packages: `@prisma/client` (the runtime that your generated Client imports) and `@prisma/adapter-pg` (the PostgreSQL driver adapter). Install both:
```shell
npm install @prisma/client@7.9.1 @prisma/adapter-pg
```
> **Note**: `@prisma/adapter-pg` pulls in the `pg` driver (and its types) for you, so there's no separate `pg` install. If you skip `@prisma/client`, the app fails at runtime with `Cannot find module '@prisma/client/runtime/client'`.
Now create a seed file called `prisma/seed.ts`:
```shell
touch prisma/seed.ts
```
Then, inside the seed file, add the following code:
```ts
// prisma/seed.ts
import 'dotenv/config';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '../generated/prisma/client';
// initialize Prisma Client with the PostgreSQL driver adapter
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
const prisma = new PrismaClient({ adapter });
async function main() {
// create two dummy articles
const post1 = await prisma.article.upsert({
where: { title: 'Prisma Adds Support for MongoDB' },
update: {},
create: {
title: 'Prisma Adds Support for MongoDB',
body: 'Support for MongoDB has been one of the most requested features since the initial release of...',
description:
"We are excited to share that today's Prisma ORM release adds stable support for MongoDB!",
published: false,
},
});
const post2 = await prisma.article.upsert({
where: { title: "What's new in Prisma? (Q1/22)" },
update: {},
create: {
title: "What's new in Prisma? (Q1/22)",
body: 'Our engineers have been working hard, issuing new releases with many improvements...',
description:
'Learn about everything in the Prisma ecosystem and community from January to March 2022.',
published: true,
},
});
console.log({ post1, post2 });
}
// execute the main function
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
// close Prisma Client at the end
await prisma.$disconnect();
});
```
Inside this script, you first initialize the PostgreSQL driver adapter and pass it to a new Prisma Client instance. Then you create two articles using the `prisma.upsert()` function. The `upsert` function will only create a new article if no article matches the `where` condition. You are using an `upsert` query instead of a `create` query because `upsert` removes errors related to accidentally trying to insert the same record twice.
You need to tell Prisma what script to execute when running the seeding command. In Prisma 7, this is configured in `prisma.config.ts` (not `package.json`). Add a `seed` key under `migrations`:
```ts
// prisma.config.ts
import "dotenv/config";
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: "tsx prisma/seed.ts",
},
datasource: {
url: process.env["DATABASE_URL"],
},
});
```
The `seed` command will execute the `prisma/seed.ts` script using `tsx`.
Execute seeding with the following command:
```shell
npx prisma db seed
```
You should see the following output:
```
Running seed command `tsx prisma/seed.ts` ...
{
post1: {
id: 1,
title: 'Prisma Adds Support for MongoDB',
...
published: false,
createdAt: 2026-06-25T07:42:14.175Z,
updatedAt: 2026-06-25T07:42:14.175Z
},
post2: {
id: 2,
title: "What's new in Prisma? (Q1/22)",
...
published: true,
createdAt: 2026-06-25T07:42:14.206Z,
updatedAt: 2026-06-25T07:42:14.206Z
}
}
🌱 The seed command has been executed.
```
> **Note**: You can learn more about seeding in the [Prisma Docs](https://www.prisma.io/docs/orm/v7/prisma-migrate/workflows/seeding).
### Create a Prisma service [#create-a-prisma-service]
Inside your NestJS application, it is good practice to abstract away the Prisma Client API from your application. To do this, you will create a new service that will contain Prisma Client. This service, called `PrismaService`, will be responsible for instantiating a `PrismaClient` instance (with the driver adapter) and connecting to your database.
The Nest CLI gives you an easy way to generate modules and services directly from the CLI. Run the following command in your terminal:
```shell
npx nest generate module prisma
npx nest generate service prisma
```
> **Note**: If necessary, refer to the NestJS docs for an introduction to [services](https://docs.nestjs.com/providers) and [modules](https://docs.nestjs.com/modules).
This should generate a new subdirectory `./src/prisma` with a `prisma.module.ts` and `prisma.service.ts` file. Update the service file to instantiate Prisma Client with the PostgreSQL adapter and connect when the module initializes:
```ts
// src/prisma/prisma.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '../../generated/prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
constructor() {
super({
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
});
}
async onModuleInit() {
await this.$connect();
}
}
```
Because the driver adapter reads `DATABASE_URL` from the environment at runtime, you need to make sure your `.env` file is loaded when the app boots. Use the NestJS `ConfigModule` for this. Install it:
```shell
npm install @nestjs/config
```
Then register it globally in your root `AppModule` (you'll add the `ArticlesModule` later in this tutorial):
```ts
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { PrismaModule } from './prisma/prisma.module';
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true }), PrismaModule],
})
export class AppModule {}
```
The Prisma module will be responsible for creating a [singleton](https://docs.nestjs.com/modules#shared-modules) instance of the `PrismaService` and allow sharing of the service throughout your application. To do this, add the `PrismaService` to the `exports` array in the `prisma.module.ts` file:
```ts
// src/prisma/prisma.module.ts
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
```
Now, any module that *imports* the `PrismaModule` will have access to `PrismaService` and can inject it into its own components/services. This is a common pattern for NestJS applications.
With that out of the way, you are done setting up Prisma! You can now get to work on building the REST API.
## Set up Swagger [#set-up-swagger]
[Swagger](https://swagger.io/) is a tool to document your API using the [OpenAPI specification](https://github.com/OAI/OpenAPI-Specification). Nest has a dedicated module for Swagger, which you will be using shortly.
Get started by installing the required dependency:
```shell
npm install @nestjs/swagger
```
> **Note**: In older versions you also had to install `swagger-ui-express` separately. With `@nestjs/swagger` v11 it's included, so a single install is enough.
Now open `main.ts` and initialize Swagger using the `SwaggerModule` class:
```ts
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Median')
.setDescription('The Median API description')
.setVersion('0.1')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
```
While the application is running, open your browser and navigate to [`http://localhost:3000/api`](http://localhost:3000/api). You should see the Swagger UI.

## Implement CRUD operations for `Article` model [#implement-crud-operations-for-article-model]
In this section, you will implement the Create, Read, Update, and Delete (CRUD) operations for the `Article` model and any accompanying business logic.
### Generate REST resources [#generate-rest-resources]
Before you can implement the REST API, you will need to generate the REST resources for the `Article` model. This can be done quickly using the Nest CLI. Run the following command in your terminal:
```shell
npx nest generate resource
```
You will be given a few CLI prompts. Answer the questions accordingly:
1. `What name would you like to use for this resource (plural, e.g., "users")?` **articles**
2. `What transport layer do you use?` **REST API**
3. `Would you like to generate CRUD entry points?` **Yes**
You should now find a new `src/articles` directory with all the boilerplate for your REST endpoints. Inside the `src/articles/articles.controller.ts` file, you will see the definition of different routes (also called route handlers). The business logic for handling each request is encapsulated in the `src/articles/articles.service.ts` file. Currently, this file contains dummy implementations.
Generating the resource also imports `ArticlesModule` into your `AppModule`. Your `app.module.ts` should now look like this:
```ts
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ArticlesModule } from './articles/articles.module';
import { PrismaModule } from './prisma/prisma.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
ArticlesModule,
PrismaModule,
],
})
export class AppModule {}
```
If you open the Swagger [API page](http://localhost:3000/api) again, you should see something like this:

The `SwaggerModule` searches for all `@Body()`, `@Query()`, and `@Param()` decorators on the route handlers to generate this API page.
### Add `PrismaClient` to the `Articles` module [#add-prismaclient-to-the-articles-module]
To access `PrismaClient` inside the `Articles` module, you must add the `PrismaModule` as an import. Add the following `imports` to `ArticlesModule`:
```ts
// src/articles/articles.module.ts
import { Module } from '@nestjs/common';
import { ArticlesService } from './articles.service';
import { ArticlesController } from './articles.controller';
import { PrismaModule } from '../prisma/prisma.module';
@Module({
controllers: [ArticlesController],
providers: [ArticlesService],
imports: [PrismaModule],
})
export class ArticlesModule {}
```
You can now inject the `PrismaService` inside the `ArticlesService` and use it to access the database. To do this, add a constructor to `articles.service.ts` like this:
```ts
// src/articles/articles.service.ts
import { Injectable } from '@nestjs/common';
import { CreateArticleDto } from './dto/create-article.dto';
import { UpdateArticleDto } from './dto/update-article.dto';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class ArticlesService {
constructor(private prisma: PrismaService) {}
// CRUD operations
}
```
### Define `GET /articles` endpoint [#define-get-articles-endpoint]
The controller for this endpoint is called `findAll`. This endpoint will return all published articles in the database. The `findAll` controller looks like this:
```ts
// src/articles/articles.controller.ts
@Get()
findAll() {
return this.articlesService.findAll();
}
```
You need to update `ArticlesService.findAll()` to return an array of all published articles in the database:
```ts
// src/articles/articles.service.ts
findAll() {
return this.prisma.article.findMany({ where: { published: true } });
}
```
The `findMany` query will return all `article` records that match the `where` condition.
You can test out the endpoint by going to [`http://localhost:3000/api`](http://localhost:3000/api) and
clicking on the **GET /articles** dropdown menu. Press **Try it out** and then **Execute** to see the result.

> **Note**: You can also run all requests in the browser directly or through a REST client (like [Postman](https://www.postman.com/)). Swagger also generates the curl commands for each request in case you want to run the HTTP requests in the terminal.
### Define `GET /articles/drafts` endpoint [#define-get-articlesdrafts-endpoint]
You will define a new route to fetch all *unpublished* articles. NestJS did not automatically generate the controller route handler for this endpoint, so you have to write it yourself.
```ts
// src/articles/articles.controller.ts
@Get('drafts')
findDrafts() {
return this.articlesService.findDrafts();
}
```
Your editor should show an error that no function called `articlesService.findDrafts()` exists. To fix this, implement the `findDrafts` method in `ArticlesService`:
```ts
// src/articles/articles.service.ts
findDrafts() {
return this.prisma.article.findMany({ where: { published: false } });
}
```
The `GET /articles/drafts` endpoint will now be available in the Swagger [API page](http://localhost:3000/api).
> **Note**: I recommend testing out each endpoint through the Swagger [API page](http://localhost:3000/api) once you finish implementing it.
### Define `GET /articles/:id` endpoint [#define-get-articlesid-endpoint]
The controller route handler for this endpoint is called `findOne`. It looks like this:
```ts
// src/articles/articles.controller.ts
@Get(':id')
findOne(@Param('id') id: string) {
return this.articlesService.findOne(+id);
}
```
The route accepts a dynamic `id` parameter, which is passed to the `findOne` controller route handler. Since the `Article` model has an integer `id` field, the `id` parameter needs to be casted to a number using the `+` operator.
Now, update the `findOne` method in the `ArticlesService` to return the article with the given id:
```ts
// src/articles/articles.service.ts
findOne(id: number) {
return this.prisma.article.findUnique({ where: { id } });
}
```
Once again, test out the endpoint by going to [`http://localhost:3000/api`](http://localhost:3000/api). Click on the **`GET /articles/{id}`** dropdown menu. Press **Try it out**, add a valid value to the **id** parameter, and press **Execute** to see the result.

### Define `POST /articles` endpoint [#define-post-articles-endpoint]
This is the endpoint for creating new articles. The controller route handler for this endpoint is called `create`. It looks like this:
```ts
// src/articles/articles.controller.ts
@Post()
create(@Body() createArticleDto: CreateArticleDto) {
return this.articlesService.create(createArticleDto);
}
```
Notice that it expects arguments of type `CreateArticleDto` in the request body. A DTO (Data Transfer Object) is an object that defines how the data will be sent over the network. Currently, the `CreateArticleDto` is an empty class. You will add properties to it to define the shape of the request body.
```ts
// src/articles/dto/create-article.dto.ts
import { ApiProperty } from '@nestjs/swagger';
export class CreateArticleDto {
@ApiProperty()
title: string;
@ApiProperty({ required: false })
description?: string;
@ApiProperty()
body: string;
@ApiProperty({ required: false, default: false })
published?: boolean = false;
}
```
The `@ApiProperty` decorators are required to make the class properties visible to the `SwaggerModule`. More information about this is available in the [NestJS docs](https://docs.nestjs.com/openapi/types-and-parameters).
The `CreateArticleDto` should now be defined in the Swagger API page under **Schemas**. The shape of `UpdateArticleDto` is automatically inferred from the `CreateArticleDto` definition. So `UpdateArticleDto` is also defined inside Swagger.

Now update the `create` method in the `ArticlesService` to create a new article in the database:
```ts
// src/articles/articles.service.ts
create(createArticleDto: CreateArticleDto) {
return this.prisma.article.create({ data: createArticleDto });
}
```
### Define `PATCH /articles/:id` endpoint [#define-patch-articlesid-endpoint]
This endpoint is for updating existing articles. The route handler for this endpoint is called `update`. It looks like this:
```ts
// src/articles/articles.controller.ts
@Patch(':id')
update(@Param('id') id: string, @Body() updateArticleDto: UpdateArticleDto) {
return this.articlesService.update(+id, updateArticleDto);
}
```
The `updateArticleDto` definition is defined as a [`PartialType`](https://docs.nestjs.com/openapi/mapped-types#partial) of `CreateArticleDto`. So it can have all the properties of `CreateArticleDto`.
```ts
// src/articles/dto/update-article.dto.ts
import { PartialType } from '@nestjs/swagger';
import { CreateArticleDto } from './create-article.dto';
export class UpdateArticleDto extends PartialType(CreateArticleDto) {}
```
Just like before, you must update the corresponding service method for this operation:
```ts
// src/articles/articles.service.ts
update(id: number, updateArticleDto: UpdateArticleDto) {
return this.prisma.article.update({
where: { id },
data: updateArticleDto,
});
}
```
The `article.update` operation will try to find an `Article` record with the given `id` and update it with the data of `updateArticleDto`.
If no such `Article` record is found in the database, Prisma will return an error. In such cases, the API does not return a user-friendly error message. You will learn about error handling with NestJS in a future tutorial.
### Define `DELETE /articles/:id` endpoint [#define-delete-articlesid-endpoint]
This endpoint is to delete existing articles. The route handler for this endpoint is called `remove`. It looks like this:
```ts
// src/articles/articles.controller.ts
@Delete(':id')
remove(@Param('id') id: string) {
return this.articlesService.remove(+id);
}
```
Just like before, go to `ArticlesService` and update the corresponding method:
```ts
// src/articles/articles.service.ts
remove(id: number) {
return this.prisma.article.delete({ where: { id } });
}
```
That was the last operation for the `articles` endpoint. Congratulations your API is almost ready! 🎉
### Group endpoints together in Swagger [#group-endpoints-together-in-swagger]
Add an `@ApiTags` decorator to the `ArticlesController` class, to group all the `articles` endpoints together in Swagger:
```ts
// src/articles/articles.controller.ts
import { ApiTags } from '@nestjs/swagger';
@Controller('articles')
@ApiTags('articles')
export class ArticlesController {
// ...
}
```
The [API page](http://localhost:3000/api/) now has the `articles` endpoints grouped together.

## Update Swagger response types [#update-swagger-response-types]
If you look at the **Responses** tab under each endpoint in Swagger, you will find that the **Description** is empty. This is because Swagger does not know the response types for any of the endpoints. You're going to fix this using a few decorators.
First, you need to define an entity that Swagger can use to identify the shape of the returned `entity` object. To do this, update the `ArticleEntity` class in the `article.entity.ts` file as follows:
```ts
// src/articles/entities/article.entity.ts
import { Article } from '../../../generated/prisma/client';
import { ApiProperty } from '@nestjs/swagger';
export class ArticleEntity implements Article {
@ApiProperty()
id: number;
@ApiProperty()
title: string;
@ApiProperty({ required: false, nullable: true })
description: string | null;
@ApiProperty()
body: string;
@ApiProperty()
published: boolean;
@ApiProperty()
createdAt: Date;
@ApiProperty()
updatedAt: Date;
}
```
This is an implementation of the `Article` type generated by Prisma Client, with `@ApiProperty` decorators added to each property. Note that the `Article` type is imported from your generated Client (`../../../generated/prisma/client`), not from `@prisma/client`.
Now, it's time to annotate the controller route handlers with the correct response types. NestJS has a set of decorators for this purpose.
```ts
// src/articles/articles.controller.ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { ArticlesService } from './articles.service';
import { CreateArticleDto } from './dto/create-article.dto';
import { UpdateArticleDto } from './dto/update-article.dto';
import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { ArticleEntity } from './entities/article.entity';
@Controller('articles')
@ApiTags('articles')
export class ArticlesController {
constructor(private readonly articlesService: ArticlesService) {}
@Post()
@ApiCreatedResponse({ type: ArticleEntity })
create(@Body() createArticleDto: CreateArticleDto) {
return this.articlesService.create(createArticleDto);
}
@Get()
@ApiOkResponse({ type: ArticleEntity, isArray: true })
findAll() {
return this.articlesService.findAll();
}
@Get('drafts')
@ApiOkResponse({ type: ArticleEntity, isArray: true })
findDrafts() {
return this.articlesService.findDrafts();
}
@Get(':id')
@ApiOkResponse({ type: ArticleEntity })
findOne(@Param('id') id: string) {
return this.articlesService.findOne(+id);
}
@Patch(':id')
@ApiOkResponse({ type: ArticleEntity })
update(@Param('id') id: string, @Body() updateArticleDto: UpdateArticleDto) {
return this.articlesService.update(+id, updateArticleDto);
}
@Delete(':id')
@ApiOkResponse({ type: ArticleEntity })
remove(@Param('id') id: string) {
return this.articlesService.remove(+id);
}
}
```
You added the `@ApiOkResponse` for `GET`, `PATCH` and `DELETE` endpoints and `@ApiCreatedResponse` for `POST` endpoints. The `type` property is used to specify the return type. You can find all the response decorators that NestJS provides in the [NestJS docs](https://docs.nestjs.com/openapi/operations#responses).
Now, Swagger should properly define the response type for all endpoints on the API page.

## Frequently asked questions [#frequently-asked-questions]
### Why does Prisma 7 generate the Client into my project instead of node_modules?
The `prisma-client` generator outputs the Client as plain source files to the `output` path you configure (here, `generated/prisma`). This makes the generated code transparent and bundler-friendly, and your application imports the Client from that path instead of from `@prisma/client`.
### Do I still need the @prisma/client package in Prisma 7?
Yes. The generated Client imports its runtime from the `@prisma/client` package, and Postgres access goes through the `@prisma/adapter-pg` driver adapter. Without `@prisma/client` installed, the app fails at runtime with `Cannot find module '@prisma/client/runtime/client'`.
### Why do I get 'exports is not defined' when NestJS loads Prisma Client?
NestJS compiles to CommonJS, while the generated Client defaults to a module format that doesn't match. Set `moduleFormat = "cjs"` on the generator block in `schema.prisma` and regenerate; the error disappears.
## Summary and final remarks [#summary-and-final-remarks]
Congratulations! You've built a rudimentary REST API using NestJS and Prisma 7. Throughout this tutorial you:
* Built a REST API with NestJS
* Smoothly integrated Prisma 7 (with a PostgreSQL driver adapter) into a NestJS project
* Documented your REST API using Swagger and OpenAPI
One of the main takeaways from this tutorial is how easy it is to build a REST API with NestJS and Prisma. This is an incredibly productive stack for rapidly building well structured, type-safe and maintainable backend applications.
In the [next part](https://www.prisma.io/blog/nestjs-prisma-validation-7D056s1kOla1) of this series, you will add input validation and transformation to this API. If you want to go deeper on the Prisma side first, start with the [Prisma getting started guide](https://www.prisma.io/docs/getting-started), learn more about [Prisma Migrate](https://www.prisma.io/docs/orm/v7/prisma-migrate/getting-started), or create a Prisma Postgres database for your next NestJS project.
Looking ahead: [Prisma 8](https://www.prisma.io/docs/orm) is a TypeScript-native rewrite of Prisma ORM, built for AI coding agents, and it is now a [release candidate](https://www.prisma.io/docs/orm/release-status). Prisma 7 remains fully supported; its docs live at [prisma.io/docs/orm/v7](https://www.prisma.io/docs/orm/v7). To start with Prisma 8, run `npm create prisma@latest` or read the [Prisma 8 docs](https://www.prisma.io/docs/getting-started).