Running on a server
On a server a project is run differently than during development. start:dev watches files and compiles TypeScript on the fly — slow and unnecessary. Instead, the code is compiled to JavaScript once and run with plain Node.js.
Building
npm ci
npm run buildnpm ci installs packages strictly from package-lock.json. npm run build compiles the src folder into dist. The main file after the build is dist/main.js.
Starting
NODE_ENV=production npm run start:prodThe start:prod script runs node dist/main. The terminal shows Nest application successfully started, and the API answers on the port from the PORT variable.
Environment variables
On a server, values are not set in the code but in the environment — in the hosting settings, in a systemd service file or in a .env created right on the server:
NODE_ENV=production
PORT=3000
DB_PATH=/var/data/blog.sqlite
JWT_SECRET=a-long-random-string-for-the-server-onlyYou can generate a long random secret with node -e "console.log(require('crypto').randomBytes(32).toString('hex'))".
Production settings
// src/app.module.ts
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'better-sqlite3',
database: config.getOrThrow<string>('DB_PATH'),
autoLoadEntities: true,
synchronize: config.get('NODE_ENV') !== 'production',
}),
}),On the server synchronize is off: tables are changed with TypeORM migrations, not automatically. Under heavy load SQLite is usually replaced with PostgreSQL — only type and the connection options change.
// src/main.ts
const app = await NestFactory.create(AppModule);
app.enableCors({ origin: 'https://example.com' });
app.enableShutdownHooks();enableCors() allows requests to the API from pages on example.com. enableShutdownHooks() lets the application close the database connection properly when the server stops the process.
Keeping the process running
If you just run node dist/main and close the terminal, the server stops. A process manager keeps it running — for example, PM2:
npm i -g pm2
pm2 start dist/main.js --name blog-api
pm2 savePM2 restarts the application after a crash, and pm2 logs blog-api shows the log. Other options are a systemd service or a Docker container.
Release steps
| Step | Command |
|---|---|
| Get the code | git pull |
| Install packages | npm ci |
| Build | npm run build |
| Apply migrations | npx typeorm migration:run -d dist/data-source.js |
| Restart | pm2 restart blog-api |
The migrations step is needed if you use them; the data-source file describes the connection for the TypeORM command-line tool.
Common mistake
The code is built, but the environment variables were never set on the server. The application crashes at startup or, worse, starts with defaults: a different database and an empty secret. Use getOrThrow() for required settings — then the server refuses to start until every needed variable is set, and the error names the missing one.