This guide covers deploying the Kioto Teteria NestJS backend to production environments, including build optimization, database setup, and platform-specific configurations.
Pre-Deployment Checklist
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)
Build the application
Ensure the application builds successfully:
Run database migrations
Apply all Prisma migrations to your production database: pnpm run prisma:generate
npx prisma migrate deploy
Test production build locally
Verify the production build runs correctly:
Build Process
The application uses the NestJS CLI for building. Configuration is defined in nest-cli.json and tsconfig.json.
Production Build
This command (defined in package.json:9):
Runs nest build which compiles TypeScript to JavaScript
Outputs compiled files to ./dist directory
Includes source maps for debugging
Removes comments from output (configured in tsconfig.json:9)
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:
Production build
Build with tree-shaking
# 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
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
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.
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
Install Railway CLI
npm install -g @railway/cli
railway login
Initialize Railway project
Add PostgreSQL database
railway add --database postgresql
Railway automatically sets DATABASE_URL.
Set environment variables
railway variables set JWT_SECRET=your-secret-key
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
Install Heroku CLI
npm install -g heroku
heroku login
Create Heroku app
heroku create kioto-teteria-backend
Add PostgreSQL addon
heroku addons:create heroku-postgresql:mini
Set environment variables
heroku config:set JWT_SECRET=your-secret-key
Add Procfile
Create Procfile in project root: release: npx prisma migrate deploy
web: pnpm run start:prod
AWS (Elastic Beanstalk)
Initialize EB application
eb init -p node.js kioto-teteria-backend
Set environment variables
eb setenv DATABASE_URL=postgresql://... JWT_SECRET=...
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
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
Clone repository
git clone https://github.com/your-org/kioto-teteria-backend.git
cd kioto-teteria-backend
Install dependencies and build
pnpm install
pnpm run build
pnpm run prisma:generate
Set up environment variables
sudo nano /etc/environment
# Add:
# DATABASE_URL="postgresql://..."
# JWT_SECRET="..."
Run database migrations
npx prisma migrate deploy
Set up PM2 for process management
sudo npm install -g pm2
pm2 start dist/main.js --name kioto-backend
pm2 startup
pm2 save
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
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 ,
});
Add Helmet for security headers
pnpm add @nestjs/throttler
import { ThrottlerModule } from '@nestjs/throttler' ;
@ Module ({
imports: [
ThrottlerModule . forRoot ([{
ttl: 60000 ,
limit: 10 ,
}]),
],
})
Environment variable validation
Install validation packages: 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
Integrate APM tools:
New Relic : Application performance monitoring
Datadog : Infrastructure and application monitoring
Sentry : Error tracking and reporting
CloudWatch : AWS-native monitoring
Troubleshooting
Solutions:
Clear build cache: rm -rf dist node_modules && pnpm install
Check TypeScript errors: npx tsc --noEmit
Verify all dependencies are installed
Check tsconfig.json configuration
Migration fails in production
Solutions:
Backup database before migrating
Run npx prisma migrate status to check state
Resolve migration conflicts manually
Use npx prisma migrate resolve for failed migrations
Never run prisma migrate dev in production
Application crashes on startup
Solutions:
Check environment variables are set
Verify database connectivity
Review application logs
Ensure Prisma Client is generated
Check port is not already in use
Solutions:
Reduce Prisma connection pool size
Implement caching for frequently accessed data
Monitor for memory leaks
Increase server resources
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