Skip to main content
This guide covers deploying the Kioto Teteria NestJS backend to production environments, including build optimization, database setup, and platform-specific configurations.

Pre-Deployment Checklist

1

Set environment variables

Configure all required environment variables in your deployment platform:
  • DATABASE_URL - PostgreSQL connection string
  • JWT_SECRET - Secure JWT signing key (min 32 chars)
  • PORT - Application port (optional, defaults to 3000)
2

Build the application

Ensure the application builds successfully:
pnpm run build
3

Run database migrations

Apply all Prisma migrations to your production database:
pnpm run prisma:generate
npx prisma migrate deploy
4

Test production build locally

Verify the production build runs correctly:
pnpm run start:prod

Build Process

The application uses the NestJS CLI for building. Configuration is defined in nest-cli.json and tsconfig.json.

Production Build

pnpm run build
This command (defined in package.json:9):
  1. Runs nest build which compiles TypeScript to JavaScript
  2. Outputs compiled files to ./dist directory
  3. Includes source maps for debugging
  4. Removes comments from output (configured in tsconfig.json:9)
  5. Generates declaration files
Build configuration (nest-cli.json:5-7):
{
  "compilerOptions": {
    "deleteOutDir": true  // Cleans dist folder before each build
  }
}
TypeScript compiler options (tsconfig.json):
  • Target: ES2023
  • Module: NodeNext (ESM + CommonJS interop)
  • Output directory: ./dist
  • Strict mode enabled
  • Decorators: Experimental (required for NestJS)

Build Optimization

For smaller bundle sizes and faster cold starts:
# Standard production build
pnpm run build

# Build output is in ./dist
ls -lh dist/
The build process automatically handles:
  • TypeScript compilation
  • Decorator metadata emission
  • Source map generation
  • Module resolution for Node.js

Database Setup

Before starting the application, set up your production database.

Generate Prisma Client

pnpm run prisma:generate
This generates the Prisma Client based on your schema (prisma/schema.prisma). Prisma configuration (prisma.config.ts):
export default defineConfig({
  schema: 'prisma/schema.prisma',
  migrations: {
    path: 'prisma/migrations',
  },
  datasource: {
    url: process.env.DATABASE_URL,
    shadowDatabaseUrl: process.env.SHADOW_DATABASE_URL,
  },
});

Run Migrations

Always backup your production database before running migrations.
# Deploy pending migrations to production
npx prisma migrate deploy

# Verify migration status
npx prisma migrate status
Migration behavior:
  • Applies all pending migrations in order
  • Creates _prisma_migrations table to track applied migrations
  • Does not generate new migrations (safe for production)
  • Exits with error if migrations fail

Database Models

The schema includes:
  • Admin - Administrative users with JWT authentication
  • Category - Product categories with slugs
  • Product - Tea products with pricing and descriptions
  • Order - Customer orders with status tracking
  • OrderItem - Individual items in orders
  • NewsletterSubscriber - Email subscribers

Starting the Application

Production Mode

pnpm run start:prod
This command (defined in package.json:14):
  • Runs the compiled JavaScript: node dist/main
  • Starts on port specified by PORT env variable (default: 3000)
  • Enables production optimizations
  • Logs startup status to console
Application bootstrap (src/main.ts:5-27):
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // Global validation pipe
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
    }),
  );
  
  const port = process.env.PORT ?? 3000;
  await app.listen(port);
  
  console.log(`🚀 Server ready at http://localhost:${port}`);
}

Development Mode

# Start with file watching
pnpm run start:dev

# Start with debugging
pnpm run start:debug
In development, use start:dev for automatic restarts on file changes.

Platform-Specific Deployment

Docker Deployment

Create a Dockerfile in your project root:
# Multi-stage build for smaller image
FROM node:20-alpine AS builder

# Install pnpm
RUN npm install -g pnpm

WORKDIR /app

# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY prisma ./prisma/

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source code
COPY . .

# Generate Prisma Client
RUN pnpm run prisma:generate

# Build application
RUN pnpm run build

# Production stage
FROM node:20-alpine

# Install pnpm
RUN npm install -g pnpm

WORKDIR /app

# Copy built application
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
COPY --from=builder /app/prisma ./prisma

# Expose port
EXPOSE 3000

# Run migrations and start app
CMD ["sh", "-c", "npx prisma migrate deploy && pnpm run start:prod"]
Docker Compose (docker-compose.yml):
version: '3.8'

services:
  backend:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://postgres:postgres@db:5432/kioto_teteria
      JWT_SECRET: ${JWT_SECRET}
      PORT: 3000
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: kioto_teteria
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  postgres_data:
Deploy with Docker:
# Build and start
docker-compose up -d

# View logs
docker-compose logs -f backend

# Stop services
docker-compose down

Railway Deployment

1

Install Railway CLI

npm install -g @railway/cli
railway login
2

Initialize Railway project

railway init
3

Add PostgreSQL database

railway add --database postgresql
Railway automatically sets DATABASE_URL.
4

Set environment variables

railway variables set JWT_SECRET=your-secret-key
5

Deploy

railway up
Railway configuration (railway.json):
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "pnpm install && pnpm run build && pnpm run prisma:generate"
  },
  "deploy": {
    "startCommand": "npx prisma migrate deploy && pnpm run start:prod",
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 10
  }
}

Heroku Deployment

1

Install Heroku CLI

npm install -g heroku
heroku login
2

Create Heroku app

heroku create kioto-teteria-backend
3

Add PostgreSQL addon

heroku addons:create heroku-postgresql:mini
4

Set environment variables

heroku config:set JWT_SECRET=your-secret-key
5

Add Procfile

Create Procfile in project root:
release: npx prisma migrate deploy
web: pnpm run start:prod
6

Deploy

git push heroku main

AWS (Elastic Beanstalk)

1

Install EB CLI

pip install awsebcli
2

Initialize EB application

eb init -p node.js kioto-teteria-backend
3

Create environment

eb create production
4

Set environment variables

eb setenv DATABASE_URL=postgresql://... JWT_SECRET=...
5

Deploy

eb deploy
Elastic Beanstalk configuration (.ebextensions/nodecommand.config):
option_settings:
  aws:elasticbeanstalk:container:nodejs:
    NodeCommand: "npm run start:prod"
    NodeVersion: 20
  aws:elasticbeanstalk:application:environment:
    NPM_USE_PRODUCTION: false

container_commands:
  01_install_pnpm:
    command: "npm install -g pnpm"
  02_install_dependencies:
    command: "pnpm install"
  03_build:
    command: "pnpm run build"
  04_prisma_generate:
    command: "pnpm run prisma:generate"
  05_migrate:
    command: "npx prisma migrate deploy"

VPS / Linux Server

1

Install Node.js and pnpm

# Install Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# Install pnpm
sudo npm install -g pnpm
2

Clone repository

git clone https://github.com/your-org/kioto-teteria-backend.git
cd kioto-teteria-backend
3

Install dependencies and build

pnpm install
pnpm run build
pnpm run prisma:generate
4

Set up environment variables

sudo nano /etc/environment
# Add:
# DATABASE_URL="postgresql://..."
# JWT_SECRET="..."
5

Run database migrations

npx prisma migrate deploy
6

Set up PM2 for process management

sudo npm install -g pm2
pm2 start dist/main.js --name kioto-backend
pm2 startup
pm2 save
7

Configure Nginx reverse proxy

server {
  listen 80;
  server_name api.kioto-teteria.com;

  location / {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}

Health Checks

Implement health check endpoints for monitoring:
// src/app.controller.ts
@Get('health')
getHealth() {
  return {
    status: 'ok',
    timestamp: new Date().toISOString(),
  };
}

@Get('health/db')
async getDatabaseHealth() {
  try {
    await this.prisma.$queryRaw`SELECT 1`;
    return { status: 'ok', database: 'connected' };
  } catch (error) {
    throw new HttpException(
      { status: 'error', database: 'disconnected' },
      HttpStatus.SERVICE_UNAVAILABLE,
    );
  }
}
Configure monitoring:
# Check application health
curl https://your-app.com/health

# Check database connectivity
curl https://your-app.com/health/db

Performance Optimization

Connection Pooling

Prisma automatically manages connection pooling. Configure pool size via DATABASE_URL:
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10"

Caching

Add Redis for caching frequently accessed data:
import { CacheModule } from '@nestjs/cache-manager';
import * as redisStore from 'cache-manager-redis-store';

@Module({
  imports: [
    CacheModule.register({
      store: redisStore,
      host: process.env.REDIS_HOST,
      port: process.env.REDIS_PORT,
    }),
  ],
})
export class AppModule {}

Logging

Configure production logging:
const app = await NestFactory.create(AppModule, {
  logger: ['error', 'warn', 'log'],
});

Security Hardening

app.enableCors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3001'],
  credentials: true,
});
pnpm add helmet
import helmet from 'helmet';
app.use(helmet());
pnpm add @nestjs/throttler
import { ThrottlerModule } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot([{
      ttl: 60000,
      limit: 10,
    }]),
  ],
})
Install validation packages:
pnpm add joi
Configure validation schema:
import * as Joi from 'joi';

ConfigModule.forRoot({
  validationSchema: Joi.object({
    DATABASE_URL: Joi.string().required(),
    JWT_SECRET: Joi.string().min(32).required(),
    PORT: Joi.number().default(3000),
  }),
})

Monitoring and Logging

Application Logs

# Docker logs
docker-compose logs -f backend

# PM2 logs
pm2 logs kioto-backend

# Heroku logs
heroku logs --tail

# Railway logs
railway logs

Performance Monitoring

Integrate APM tools:
  • New Relic: Application performance monitoring
  • Datadog: Infrastructure and application monitoring
  • Sentry: Error tracking and reporting
  • CloudWatch: AWS-native monitoring

Troubleshooting

Solutions:
  1. Clear build cache: rm -rf dist node_modules && pnpm install
  2. Check TypeScript errors: npx tsc --noEmit
  3. Verify all dependencies are installed
  4. Check tsconfig.json configuration
Solutions:
  1. Backup database before migrating
  2. Run npx prisma migrate status to check state
  3. Resolve migration conflicts manually
  4. Use npx prisma migrate resolve for failed migrations
  5. Never run prisma migrate dev in production
Solutions:
  1. Check environment variables are set
  2. Verify database connectivity
  3. Review application logs
  4. Ensure Prisma Client is generated
  5. Check port is not already in use
Solutions:
  1. Reduce Prisma connection pool size
  2. Implement caching for frequently accessed data
  3. Monitor for memory leaks
  4. Increase server resources
  5. Enable Node.js heap snapshots for analysis

Next Steps

Environment Variables

Configure environment-specific settings

Database Management

Learn about database schema and migrations

API Reference

Explore API endpoints and authentication

Core Concepts

Understand authentication and authorization

Build docs developers (and LLMs) love