# Deploy a NestJS API with a database

Source: https://naijacloud.com/guides/nestjs-api-postgres

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 min · Beginner · Last verified 2026-10-11 · Stack: nestjs, prisma, postgres_

**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.

## 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

```sh
$ 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 title="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())
}
```

```ts title="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 },
});
```

> **Warning: Keep prisma.config.ts out of the Nest build**
>
> Add `"prisma.config.ts"` to the `exclude` list in `tsconfig.build.json`. Otherwise TypeScript compiles it too, the output moves to `dist/src/main.js`, and `npm run start:prod` fails with `Cannot find module '/app/dist/main'`.

## Write the API

A Prisma service Nest can inject:

```ts title="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:

```ts title="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 } });
  }
}
```

```ts 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`:

```ts title="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:

```sh
$ 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 command**: `npm install && npm run build`
- **Start command**: `npx prisma migrate deploy && npm run start:prod`

> **Note: Why npm install and not npm ci**
>
> The Nest starter's test tools pull in packages that only install on some platforms. A `package-lock.json` written on macOS leaves out the Linux ones, and `npm ci` then stops the build with “package.json and package-lock.json are not in sync”. `npm install` fills them in.

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:

```sh
$ 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.

## Reference

- [Deploy a web service](https://naijacloud.com/docs/deploy/web-services.md)
- [Builds](https://naijacloud.com/docs/deploy/builds.md)
- [Connecting to a database](https://naijacloud.com/docs/databases/connecting.md)
- [Environment variables](https://naijacloud.com/docs/deploy/environment-variables.md)

All guides: https://naijacloud.com/guides · Index for agents: https://naijacloud.com/llms.txt
