Deploying a NestJS App
NestJS is a progressive, enterprise-grade Node.js framework for building efficient, scalable, and maintainable server-side applications. Built with TypeScript and inspired by Angular architecture, NestJS provides a modular design pattern, extensive decorator-based configuration, dependency injection, and comprehensive tooling for building robust APIs, microservices, and real-time applications. It’s trusted by organizations worldwide for mission-critical backend systems.
This comprehensive guide walks you through deploying a NestJS application to Klutch.sh, covering both automatic Nixpacks-based deployments and Docker-based deployments. You’ll learn installation steps, explore sample code, configure environment variables, and discover best practices for production deployments.
Table of Contents
- Prerequisites
- Getting Started: Install NestJS
- Sample Code Examples
- Project Structure
- Deploying Without a Dockerfile (Nixpacks)
- Deploying With a Dockerfile
- Environment Variables & Configuration
- Modules & Dependency Injection
- Troubleshooting
- Resources
Prerequisites
To deploy a NestJS application on Klutch.sh, ensure you have:
- Node.js 18 or higher - NestJS requires a modern Node.js version for optimal performance
- npm or yarn - For managing dependencies
- Git - For version control
- GitHub account - Klutch.sh integrates with GitHub for continuous deployments
- Klutch.sh account - Sign up for free
- NestJS CLI (optional) - For easier project scaffolding and code generation
Getting Started: Install NestJS
Create a New NestJS Project
Follow these steps to create and set up a new NestJS application:
-
Install the NestJS CLI globally (optional but recommended):
Terminal window npm install -g @nestjs/cli -
Create a new NestJS project:
Terminal window nest new my-nest-appcd my-nest-appOr without the CLI:
Terminal window npm i -g @nestjs/clinpx @nestjs/cli@latest new my-nest-app -
Install dependencies (if not auto-installed):
Terminal window npm install -
Start the development server:
Terminal window npm run start:dev - Visit http://localhost:3000 to see your app running. The CLI creates a basic application with a hello world endpoint.
Sample Code Examples
Basic NestJS Application with Multiple Routes
Here’s a complete example with controllers, services, and request validation:
import { Injectable } from '@nestjs/common';
@Injectable()export class AppService { getHello(): string { return 'Hello from NestJS on Klutch.sh!'; }
getHealth() { return { status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime() }; }}import { Controller, Get, Post, Body, Param } from '@nestjs/common';import { AppService } from './app.service';
@Controller()export class AppController { constructor(private readonly appService: AppService) {}
@Get() getHello(): string { return this.appService.getHello(); }
@Get('/health') getHealth() { return this.appService.getHealth(); }
@Get('/api/users/:id') getUser(@Param('id') id: string) { return { id: parseInt(id), name: 'John Doe', email: 'john@example.com' }; }
@Post('/api/users') createUser(@Body() userData: { name: string; email: string }) { return { id: Math.floor(Math.random() * 10000), ...userData, created: new Date().toISOString() }; }}NestJS with Database Integration
import { Injectable } from '@nestjs/common';
@Injectable()export class DatabaseService { async query(sql: string): Promise<any[]> { // Your database query logic here return []; }
async findAll(table: string): Promise<any[]> { return this.query(`SELECT * FROM ${table}`); }
async findById(table: string, id: number): Promise<any> { return this.query(`SELECT * FROM ${table} WHERE id = ${id}`); }}import { Injectable } from '@nestjs/common';import { DatabaseService } from '../database/database.service';
@Injectable()export class UsersService { constructor(private databaseService: DatabaseService) {}
async getAllUsers() { return this.databaseService.findAll('users'); }
async getUserById(id: number) { return this.databaseService.findById('users', id); }}import { Controller, Get, Param } from '@nestjs/common';import { UsersService } from './users.service';
@Controller('api/users')export class UsersController { constructor(private usersService: UsersService) {}
@Get() async getAllUsers() { return this.usersService.getAllUsers(); }
@Get(':id') async getUserById(@Param('id') id: string) { return this.usersService.getUserById(parseInt(id)); }}NestJS with Guard and Middleware
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';import { Request } from 'express';
@Injectable()export class AuthGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { const request = context.switchToHttp().getRequest<Request>(); const token = request.headers.authorization?.split(' ')[1];
if (!token) { return false; }
// Validate token return true; }}import { Controller, Get, UseGuards } from '@nestjs/common';import { AuthGuard } from './auth/auth.guard';import { AppService } from './app.service';
@Controller()export class AppController { constructor(private readonly appService: AppService) {}
@Get() getHello(): string { return this.appService.getHello(); }
@Get('/protected') @UseGuards(AuthGuard) getProtectedResource() { return { data: 'This is protected' }; }}Project Structure
A typical NestJS project has this structure:
my-nest-app/├── src/│ ├── modules/│ │ ├── users/│ │ │ ├── users.controller.ts│ │ │ ├── users.service.ts│ │ │ ├── users.module.ts│ │ │ └── dto/│ │ ├── auth/│ │ │ ├── auth.guard.ts│ │ │ ├── auth.service.ts│ │ │ └── auth.module.ts│ │ └── database/│ ├── common/│ │ ├── filters/│ │ ├── guards/│ │ ├── interceptors/│ │ └── pipes/│ ├── app.controller.ts│ ├── app.service.ts│ ├── app.module.ts│ └── main.ts├── dist/├── node_modules/├── .env├── .gitignore├── tsconfig.json├── package.json└── package-lock.jsonDeploying Without a Dockerfile
Klutch.sh uses Nixpacks to automatically detect and build your NestJS application. This is the simplest deployment option that requires no additional configuration files.
-
Build your NestJS application for production to ensure everything compiles correctly:
Terminal window npm run buildVerify the
distfolder is created with compiled code. - Push your NestJS application to a GitHub repository with all your source code, `src` folder, `package.json`, and `package-lock.json` files.
- Log in to your Klutch.sh dashboard.
- Create a new project and give it a name (e.g., "My NestJS API").
-
Create a new app with the following configuration:
- Repository - Select your NestJS GitHub repository and the branch to deploy
- Traffic Type - Select HTTP (for web applications serving HTTP traffic)
- Internal Port - Set to 3000 (the default port for NestJS applications)
- Region - Choose your preferred region for deployment
- Compute - Select the appropriate compute resource size
- Instances - Choose how many instances to run (start with 1 for testing)
- Environment Variables - Add any environment variables your app needs (database URLs, API keys, secrets, etc.)
If you need to customize the start command or build process, you can set Nixpacks environment variables:
START_COMMAND: Override the default start command (e.g.,node dist/main.js)BUILD_COMMAND: Override the default build command (e.g.,npm run build)
- Click "Create" to deploy. Klutch.sh will automatically detect your Node.js project, install dependencies, build your NestJS application, and start it.
- Once deployed, your app will be available at a URL like `example-app.klutch.sh`. Test it by visiting the URL in your browser and checking the health endpoint at `/health`.
Deploying With a Dockerfile
If you prefer more control over the build and runtime environment, you can use a Dockerfile. Klutch.sh will automatically detect and use any Dockerfile in your repository’s root directory.
-
Create a `Dockerfile` in your project root:
# Multi-stage build for optimized NestJS deploymentFROM node:18-alpine AS builderWORKDIR /app# Copy package filesCOPY package*.json ./# Install dependenciesRUN npm ci# Copy source codeCOPY . .# Build NestJS applicationRUN npm run build# Production stageFROM node:18-alpineWORKDIR /app# Copy package filesCOPY package*.json ./# Install only production dependenciesRUN npm ci --only=production# Copy built application from builderCOPY --from=builder /app/dist ./dist# Set production environmentENV NODE_ENV=production# Expose port (must match your app's listening port)EXPOSE 3000# Health checkHEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \CMD node -e "require('http').get('http://localhost:3000/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"# Start the applicationCMD ["node", "dist/main.js"]
-
Create a `.dockerignore` file to exclude unnecessary files from the Docker build:
node_modulesnpm-debug.logdist.git.gitignoresrctsconfig.jsonREADME.md.env.env.local.vscode.idea.DS_Store
- Push your code (with Dockerfile and .dockerignore) to GitHub.
-
Follow the same deployment steps as the Nixpacks method:
- Log in to Klutch.sh
- Create a new project
- Create a new app pointing to your GitHub repository
- Set the traffic type to HTTP and internal port to 3000
- Add any required environment variables
- Click “Create”
Klutch.sh will automatically detect your Dockerfile and use it to build and deploy your application.
- Your deployed app will be available at `example-app.klutch.sh` once the build and deployment complete.
Environment Variables & Configuration
NestJS applications use environment variables for configuration. Set these in the Klutch.sh dashboard during app creation or update them afterward.
Common Environment Variables
# Server configurationPORT=3000NODE_ENV=production
# Database connectionDATABASE_URL=postgresql://user:password@db.example.com:5432/mydbDATABASE_HOST=db.example.comDATABASE_PORT=5432DATABASE_NAME=mydbDATABASE_USER=userDATABASE_PASSWORD=password
# API Keys and SecretsAPI_KEY=your_api_key_hereSECRET_KEY=your_secret_key_hereJWT_SECRET=your_jwt_secret_hereJWT_EXPIRATION=3600
# CORS configurationCORS_ORIGIN=https://yourdomain.com
# LoggingLOG_LEVEL=debugLOG_FORMAT=json
# Application settingsDEBUG=falseENABLE_LOGGING=trueUsing Environment Variables in Your App
import { NestFactory } from '@nestjs/core';import { AppModule } from './app.module';
async function bootstrap() { const app = await NestFactory.create(AppModule);
const port = process.env.PORT || 3000; const environment = process.env.NODE_ENV || 'development';
app.enableCors({ origin: process.env.CORS_ORIGIN || '*' });
await app.listen(port); console.log(`Server running in ${environment} mode on port ${port}`);}
bootstrap();import { registerAs } from '@nestjs/config';
export default registerAs('database', () => ({ host: process.env.DATABASE_HOST || 'localhost', port: parseInt(process.env.DATABASE_PORT || '5432'), database: process.env.DATABASE_NAME || 'mydb', username: process.env.DATABASE_USER || 'user', password: process.env.DATABASE_PASSWORD, synchronize: process.env.NODE_ENV !== 'production', logging: process.env.NODE_ENV === 'development'}));Customizing Build and Start Commands with Nixpacks
If using Nixpacks deployment without a Dockerfile, you can customize build and start commands by setting environment variables:
BUILD_COMMAND: npm run buildSTART_COMMAND: node dist/main.jsSet these as environment variables during app creation on Klutch.sh.
Modules & Dependency Injection
NestJS’s modular architecture and dependency injection system make it easy to organize and scale applications:
Creating Custom Modules
import { Module } from '@nestjs/common';import { UsersService } from './users.service';import { UsersController } from './users.controller';
@Module({ providers: [UsersService], controllers: [UsersController], exports: [UsersService]})export class UsersModule {}import { Module } from '@nestjs/common';import { ConfigModule } from '@nestjs/config';import { AppController } from './app.controller';import { AppService } from './app.service';import { UsersModule } from './users/users.module';import { DatabaseModule } from './database/database.module';
@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env' }), DatabaseModule, UsersModule ], controllers: [AppController], providers: [AppService]})export class AppModule {}Scaling with Multiple Instances
Klutch.sh makes it easy to scale your NestJS application:
- Multiple Instances - Set the number of instances in the app configuration to distribute traffic across multiple containers
- Load Balancing - Klutch.sh automatically load balances traffic across all instances
- Stateless Design - Ensure your application is stateless so it can run on multiple instances
- Shared Resources - Use external services (Redis, databases) for shared state across instances
Performance Optimization Tips
- Use Request Caching - Cache responses for frequently accessed endpoints
- Implement Rate Limiting - Protect your API from abuse
- Database Connection Pooling - Manage database connections efficiently
- Async Operations - Use async/await for non-blocking operations
- Compression - Enable gzip compression for responses
- Logging Strategy - Use structured logging for better observability
- Module Organization - Keep modules small and focused
Example with performance optimizations:
import { NestFactory } from '@nestjs/core';import { AppModule } from './app.module';import * as compression from 'compression';
async function bootstrap() { const app = await NestFactory.create(AppModule);
// Enable compression app.use(compression());
// Set security headers app.use((req, res, next) => { res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); next(); });
const port = process.env.PORT || 3000; await app.listen(port); console.log(`Server started on port ${port}`);}
bootstrap();Troubleshooting
Application Won’t Start
Problem - Deployment completes but the app shows as unhealthy
Solution:
- Check that your app listens on port 3000 (or the port you specified)
- Verify that
process.env.PORTis properly used in your main.ts - Ensure the
distfolder is created with compiled code - Check that all imports are correct and no circular dependencies exist
- Check application logs in the Klutch.sh dashboard for error messages
- Verify
package.jsonhas a validstartorstart:prodscript
Build Fails with “Cannot find module”
Problem - Dependencies are not installed during build
Solution:
- Ensure your
package.jsonis in the root directory - Verify all dependencies are listed in
dependencies, not justdevDependencies - Check that dependency names are spelled correctly
- Ensure
package-lock.jsonexists and is up to date - Run
npm installlocally to verify dependencies work
TypeScript Compilation Errors
Problem - Build fails with TypeScript errors
Solution:
- Verify
tsconfig.jsonhas correct settings - Check that all type definitions are installed
- Run
npm run buildlocally to identify errors - Ensure TypeScript version compatibility with your Node.js version
High Memory Usage
Problem - App uses excessive memory and gets killed
Solution:
- Check for memory leaks in your application code
- Implement proper cleanup for connections and event listeners
- Monitor memory usage during development with the
--inspectflag - Consider implementing request/response size limits
- Consider upgrading compute tier or reducing instance connections
Slow API Response Times
Problem - API endpoints respond slowly despite NestJS being fast
Solution:
- Check for synchronous operations that should be async
- Implement caching for frequently accessed data
- Enable compression middleware to reduce payload size
- Profile your application to identify bottlenecks
- Optimize database queries and add indexes
- Consider upgrading your compute tier
Resources
- NestJS Official Documentation
- NestJS Getting Started
- NestJS Controllers Guide
- NestJS Providers and Dependency Injection
- NestJS Modules Guide
- Nixpacks Documentation
- Klutch.sh Dashboard
Summary
Deploying a NestJS application on Klutch.sh is straightforward whether you choose Nixpacks or Docker. Both methods provide reliable, scalable hosting for your enterprise-grade Node.js applications. Start with Nixpacks for simplicity and rapid deployments, or use Docker for complete control over your build environment. With NestJS’s modular architecture and Klutch.sh’s automatic scaling, load balancing, and environment management, you can deploy your application from development to production and scale it to handle millions of requests with ease.