Configuration and environment variables
The port, the database path and secret keys differ between your computer and the server, so they are not written in code. They are passed through environment variables — values the operating system gives a process when it starts. In Node.js they are available in the process.env object.
The .env file
Create a .env file in the project root:
PORT=3000
DB_FILE=blog.db
JWT_SECRET=replace-with-a-long-random-stringEach line is a NAME=value variable. A file with real secrets is never added to Git: add the line .env to .gitignore.
Loading with --env-file
Node.js 22 can read such a file itself. Change the scripts in package.json:
{
"scripts": {
"dev": "node --watch --env-file=.env src/server.js",
"start": "node --env-file=.env src/server.js"
}
}Now the variables are available in code:
// src/server.js
import { buildApp } from './app.js';
const app = buildApp({ logger: true });
const port = Number(process.env.PORT ?? 3000);
await app.listen({ port });Change the port in .env to 3001 and restart npm run dev — the server opens on the new port. All values in process.env are strings, so the port is converted to a number.
Checking settings: @fastify/env
If you forget a variable, the error shows up not at startup but later, in a random place. The @fastify/env plugin checks variables against a JSON Schema at startup:
npm install @fastify/env// src/app.js
import Fastify from 'fastify';
import fastifyEnv from '@fastify/env';
const envSchema = {
type: 'object',
required: ['JWT_SECRET'],
properties: {
PORT: { type: 'integer', default: 3000 },
DB_FILE: { type: 'string', default: 'blog.db' },
JWT_SECRET: { type: 'string', minLength: 16 },
},
};
export function buildApp(options = {}) {
const app = Fastify(options);
app.register(fastifyEnv, { schema: envSchema });
// ...other plugins and routes
return app;
}The plugin reads process.env, converts values to the right types and puts the result in app.config. Without JWT_SECRET the server does not start and says which variable is missing.
Using it
Settings appear after the plugins have loaded, so in src/server.js wait for ready() first:
// src/server.js
const app = buildApp({ logger: true });
await app.ready();
await app.listen({ port: app.config.PORT });In routes and plugins the values are available as app.config.DB_FILE.
Common mistake
A .env file with secrets ends up in Git, and from there with strangers. Keep only a .env.example with empty values in the repository, and create the real .env on each computer and server separately.