Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 build

npm 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:prod

The 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-only

You 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 save

PM2 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

StepCommand
Get the codegit pull
Install packagesnpm ci
Buildnpm run build
Apply migrationsnpx typeorm migration:run -d dist/data-source.js
Restartpm2 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.

Thanks for reading!

Donate to the site — we work hard for you.