Building a REST API with NestJS and Prisma
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, generates the Prisma Client into your project (instead of
node_modules), and is configured through aprisma.config.tsfile. Every command and code block below was run end-to-end againstprisma@7.8,@prisma/client@7.8and@nestjs/core@11. If you're starting fresh, pair Prisma ORM with Prisma Postgres for a managed database that works out of the box.
Introduction
In this tutorial, you will learn how to build the backend REST API for a blog application called "Median" (a simple Medium 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
You will be using the following tools to build this application:
- NestJS as the backend framework
- Prisma ORM as the Object-Relational Mapper (ORM)
- PostgreSQL as the database (via Prisma Postgres or your own instance)
- Swagger as the API documentation tool
- TypeScript as the programming language
Prerequisites
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 in the NestJS docs.
Development environment
To follow along with this tutorial, you will be expected to:
- ... have Node.js (v18 or higher) installed.
- ... have a PostgreSQL database. The easiest option is Prisma Postgres; you can also run Postgres locally with Docker or a native install.
- ... have the Prisma VSCode Extension 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
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:
npx @nestjs/cli new medianThe 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.jsonMost 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:
npm run start:devThis 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/. 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
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:
npx create-dbThis 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.
Note: Prefer to run Postgres locally with no cloud at all?
npx prisma devstarts a local Prisma Postgres server in your terminal. It works for this tutorial, but the local server doesn't support theprisma migrate devworkflow you'll use later, so runnpx prisma db pushinstead ofmigrate devif you go this route. For the smoothest path through every step below, usenpx 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:
touch docker-compose.ymlAdd the following configuration inside the file:
# 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
imageoption defines what Docker image to use. Here, you are using thepostgresimage version 17. - The
environmentoption 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
volumesoption is used for persisting data in the host file system. - The
portsoption 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 port5432of the host machine to port5432of thepostgrescontainer.5432is conventionally the port used by PostgreSQL.
Make sure that nothing is running on port 5432 of your machine, then start the container:
docker compose up -dCongratulations 🎉. You now have your own PostgreSQL database to play around with!
Set up Prisma
Now that the database is ready, it's time to set up 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:
npm install -D prisma tsx dotenvYou can initialize Prisma inside your project by running:
npx prisma init --datasource-provider postgresqlThis 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
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:
# .env
DATABASE_URL="postgres://<user>:<password>@db.prisma.io:5432/postgres?sslmode=require"If you're using Docker (Option B), use the connection string for that instance:
# .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.
Now open prisma.config.ts. Prisma generated it to read DATABASE_URL from your environment via dotenv. It should look like this:
// prisma.config.ts
// This file was generated by Prisma, and assumes you have installed the following:
// npm install --save-dev prisma 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
If you open prisma/schema.prisma, you should see the following default schema:
// 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 inprisma.config.ts. - Generator: Indicates that you want to generate Prisma Client, a type-safe query builder for your database. In Prisma 7, the
prisma-clientgenerator outputs the Client as source files into your project (here,../generated/prisma) rather than intonode_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/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 likeReferenceError: exports is not defined in ES module scopewhen 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.
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/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
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:
npx prisma migrate dev --name initThis command will do two things:
- 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/migrationsfolder. - 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.
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:
npx prisma generateYou should see output like this:
✔ Generated Prisma Client (7.8.0) to ./generated/prismaNote: In Prisma 7,
migrate devapplies migrations but does not regenerate Prisma Client into your configuredoutputfolder. Runnpx prisma generateafter 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:
-- 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
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:
npm install @prisma/client @prisma/adapter-pgNote:
@prisma/adapter-pgpulls in thepgdriver (and its types) for you, so there's no separatepginstall. If you skip@prisma/client, the app fails at runtime withCannot find module '@prisma/client/runtime/client'.
Now create a seed file called prisma/seed.ts:
touch prisma/seed.tsThen, inside the seed file, add the following code:
// 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:
// 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:
npx prisma db seedYou 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.
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:
npx nest generate module prisma
npx nest generate service prismaNote: If necessary, refer to the NestJS docs for an introduction to services and 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:
// 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:
npm install @nestjs/configThen register it globally in your root AppModule (you'll add the ArticlesModule later in this tutorial):
// 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 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:
// 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
Swagger is a tool to document your API using the OpenAPI specification. Nest has a dedicated module for Swagger, which you will be using shortly.
Get started by installing the required dependency:
npm install @nestjs/swaggerNote: In older versions you also had to install
swagger-ui-expressseparately. With@nestjs/swaggerv11 it's included, so a single install is enough.
Now open main.ts and initialize Swagger using the SwaggerModule class:
// 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. You should see the Swagger UI.

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
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:
npx nest generate resourceYou will be given a few CLI prompts. Answer the questions accordingly:
What name would you like to use for this resource (plural, e.g., "users")?articlesWhat transport layer do you use?REST APIWould 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:
// 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 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
To access PrismaClient inside the Articles module, you must add the PrismaModule as an import. Add the following imports to ArticlesModule:
// 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:
// 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
The controller for this endpoint is called findAll. This endpoint will return all published articles in the database. The findAll controller looks like this:
// 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:
// 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 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). 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
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.
// 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:
// 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.
Note: I recommend testing out each endpoint through the Swagger API page once you finish implementing it.
Define GET /articles/:id endpoint
The controller route handler for this endpoint is called findOne. It looks like this:
// 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:
// 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. 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
This is the endpoint for creating new articles. The controller route handler for this endpoint is called create. It looks like this:
// 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.
// 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.
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:
// src/articles/articles.service.ts
create(createArticleDto: CreateArticleDto) {
return this.prisma.article.create({ data: createArticleDto });
}Define PATCH /articles/:id endpoint
This endpoint is for updating existing articles. The route handler for this endpoint is called update. It looks like this:
// 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 of CreateArticleDto. So it can have all the properties of CreateArticleDto.
// 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:
// 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
This endpoint is to delete existing articles. The route handler for this endpoint is called remove. It looks like this:
// 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:
// 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
Add an @ApiTags decorator to the ArticlesController class, to group all the articles endpoints together in Swagger:
// src/articles/articles.controller.ts
import { ApiTags } from '@nestjs/swagger';
@Controller('articles')
@ApiTags('articles')
export class ArticlesController {
// ...
}The API page now has the articles endpoints grouped together.

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:
// 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.
// 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.
Now, Swagger should properly define the response type for all endpoints on the API page.

Frequently asked questions
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 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, learn more about Prisma Migrate, or create a Prisma Postgres database for your next NestJS project.
Looking ahead: Prisma Next 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.
Build your next app with Prisma
Start free. Scale when you’re ready.
