Deploy a NestJS API with a database

This guide deploys a NestJS REST API to Naijacloud with a managed Postgres database. At the end you’ll have a small tasks API at your own naijacloud.app address, reading and writing Postgres through Prisma, with interactive OpenAPI docs at /docs and migrations applied on every deploy. The stack is NestJS 11, Prisma 7 and Postgres 18, deployed from a GitHub repository.

15 minBeginnerLast verified 2026-10-11nestjsprismapostgres
What you'll have at the end

A tasks API on your own naijacloud.app address. POST /tasks saves a task to Postgres, GET /tasks lists them, and /docs is the Swagger UI for both.

Deploy this templateNestJS + Postgres API template

Before you start

  • A NestJS app in a GitHub repository, with its package-lock.json committed. Starting fresh? Run npx @nestjs/cli@11 new tasks-api and push it.
  • Node.js 20 or newer on your machine, and Docker if you want a Postgres to develop against.
  • A Naijacloud account. The free tier covers this guide: one web service and one database.

Add Prisma and Swagger

Terminal
$ npm install @prisma/client@7 @prisma/adapter-pg@7 @nestjs/swagger@11
$ npm install --save-dev prisma@7

Create the schema. Nest compiles to CommonJS, so ask Prisma for a CommonJS client, generated into src/generated/prisma (add that folder to .gitignore):

prisma/schema.prisma
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
moduleFormat = "cjs"
}
datasource db {
provider = "postgresql"
}
model Task {
id Int @id @default(autoincrement())
title String
done Boolean @default(false)
createdAt DateTime @default(now())
}
prisma.config.ts
import { defineConfig } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: { path: 'prisma/migrations' },
datasource: { url: process.env.DATABASE_URL },
});

Write the API

A Prisma service Nest can inject:

src/prisma.service.ts
import { Injectable, OnModuleDestroy } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from './generated/prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleDestroy {
constructor() {
super({
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
});
}
async onModuleDestroy() {
await this.$disconnect();
}
}

The controller, and a module that wires the two together:

src/tasks.controller.ts
import { Body, Controller, Get, Post } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';
import { PrismaService } from './prisma.service';
class CreateTaskDto {
@ApiProperty({ example: 'Ship the API' })
title!: string;
}
@Controller('tasks')
export class TasksController {
constructor(private readonly prisma: PrismaService) {}
@Get()
list() {
return this.prisma.task.findMany({ orderBy: { createdAt: 'desc' } });
}
@Post()
create(@Body() dto: CreateTaskDto) {
return this.prisma.task.create({ data: { title: dto.title } });
}
}
src/app.module.ts
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { TasksController } from './tasks.controller';
@Module({
controllers: [TasksController],
providers: [PrismaService],
})
export class AppModule {}

Listen on the PORT Naijacloud gives you, on every interface, and serve the docs at /docs:

src/main.ts
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks();
const config = new DocumentBuilder().setTitle('Tasks API').build();
SwaggerModule.setup('docs', app, SwaggerModule.createDocument(app, config));
await app.listen(process.env.PORT ?? 3000, '0.0.0.0');
}
void bootstrap();

Create the first migration

Make npm run build generate the client first, by setting "build": "prisma generate && nest build" in package.json. Then create the migration against a local Postgres and commit everything:

Terminal
$ docker run -d --name tasks-db -p 5432:5432 -e POSTGRES_PASSWORD=pw -e POSTGRES_DB=tasks postgres:18-alpine
$ export DATABASE_URL=postgresql://postgres:pw@localhost:5432/tasks
$ npx prisma migrate dev --name init
$ git add .
$ git commit -m "Tasks API on Postgres"
$ git push

Create the database

In the dashboard, open your project and choose New service → Database. Name it tasks-db, pick Postgres, choose the Free size and click Create database. On the tasks-db is ready. screen, click Reveal secrets and copy the Internal URL. The password is shown once on this page.

Create the web service

Choose New service → Web service, name it tasks-api and pick your repository and branch on the GitHub tab. Naijacloud detects a Node app and fills in npm ci && npm run build and npm start. Change both. npm start runs nest start, which compiles TypeScript again at boot, so point the start command at the compiled build and run migrations first:

Build commandnpm install && npm run build
Start commandnpx prisma migrate deploy && npm run start:prod

Under Environment variables, add DATABASE_URL with the Internal URL you copied. Leave the size on Free and click Create web service.

Call the API

When the deploy is live, create a task and list it back:

Terminal
$ curl -X POST https://tasks-api.naijacloud.app/tasks \
-H 'content-type: application/json' -d '{"title":"Ship the API"}'
{"id":1,"title":"Ship the API","done":false,"createdAt":"2026-10-07T03:52:26.996Z"}
$ curl https://tasks-api.naijacloud.app/tasks
[{"id":1,"title":"Ship the API","done":false,"createdAt":"2026-10-07T03:52:26.996Z"}]

Open https://tasks-api.naijacloud.app/docs for the Swagger UI. Every push to your branch now builds, migrates and goes live. Free apps may sleep when nobody calls them for a while and wake on the next request; paid apps never sleep.

Whatever you build it with, it ships here.

Deploy your app for free

Free tier forever · No card