Skip to Content

Configuration

Validated, typed configuration that fails fast at startup.

defineConfig pairs a name with a schema and returns a typed injection token.

config.ts
import { defineConfig } from '@sukujs/core';
import { z } from 'zod';

export const Env = defineConfig(
  'env',
  z.object({
    PORT: z.coerce.number().int().positive().default(3000),
    DATABASE_URL: z.url(),
    LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
  }),
);

Add Env.load() to your features to validate the environment when the application starts.

const app = await Suku.create({ features: [Env.load(), UsersFeature], adapter: nodeAdapter() });
await app.listen(app.get(Env).PORT);

Inject it with the token.

import type { Infer } from '@sukujs/core';

@Service()
export class Database {
  constructor(@Inject(Env) private readonly env: Infer<typeof Env.schema>) {}
}

route.get('/info', {
  services: { env: Env },
  handle: ({ env }) => ({ logLevel: env.LOG_LEVEL }),   // typed from the schema
});

Failing fast

If the environment is invalid, Suku.create rejects before anything listens, and every problem is listed at once.

ConfigError: Invalid configuration "env":
  - PORT: Invalid input: expected number, received NaN
  - DATABASE_URL: Invalid input: expected string, received undefined

Other sources

load() reads process.env by default. Pass any object instead.

Env.load({ ...process.env, ...(await readSecrets()) });

Env.parse() validates and returns the value without creating a feature, for scripts that run outside the container.

Several configs

Define one per concern. Each is its own token.

export const Mail = defineConfig('mail', z.object({ SMTP_URL: z.url() }));

features: [Env.load(), Mail.load()];

On this page