Skip to main content
Search...

configuration

Complete reference for every duck-gen.json configuration option with examples and explanations.

Overview

Duck Gen reads its configuration from a duck-gen.json file in your project root. This file is required. The CLI fails if it cannot find or parse it.

All paths inside the config are resolved relative to the config file location.

Minimal config

The smallest working configuration for a NestJS project:

duck-gen.json
{
  "$schema": "node_modules/@gentleduck/gen/duck-gen.schema.json",
  "framework": "nestjs",
  "extensions": {
    "shared": {
      "includeNodeModules": false,
      "outputSource": "./generated",
      "sourceGlobs": ["src/**/*.ts"],
      "tsconfigPath": "./tsconfig.json"
    },
    "apiRoutes": {
      "enabled": true,
      "globalPrefix": "/api",
      "normalizeAnyToUnknown": true,
      "outputSource": "./generated"
    },
    "messages": {
      "enabled": true,
      "outputSource": "./generated"
    }
  }
}

JSON Schema

Add the $schema field to get autocomplete and validation in VS Code and other editors that support JSON Schema:

{
  "$schema": "node_modules/@gentleduck/gen/duck-gen.schema.json"
}

This points to the schema file bundled with the @gentleduck/gen package.

Root options

framework

{ "framework": "nestjs" }
PropertyValue
Typestring
RequiredYes
Accepted values"nestjs"

The target framework adapter to use. Currently only nestjs is supported. Other values fail config validation. More adapters are planned.

Shared options

The extensions.shared section configures settings that apply to both the API routes and messages extensions.

tsconfigPath

{
  "extensions": {
    "shared": {
      "tsconfigPath": "./tsconfig.json"
    }
  }
}
PropertyValue
Typestring
RequiredYes (for NestJS)
DefaultNone

Path to your tsconfig.json file. Duck Gen uses ts-morph to create an in-memory TypeScript project from this config. The include and exclude globs in your tsconfig.json determine which files get scanned.

Make sure your tsconfig includes the source files you want scanned. If a controller file is excluded by your tsconfig, Duck Gen won't see it.

includeNodeModules

{
  "extensions": {
    "shared": {
      "includeNodeModules": false
    }
  }
}
PropertyValue
Typeboolean
RequiredYes
Recommendedfalse

Whether to include node_modules files when scanning. Set to false unless you have a specific reason to scan third-party code (e.g. scanning a local workspace package).

outputSource

{
  "extensions": {
    "shared": {
      "outputSource": "./generated"
    }
  }
}
PropertyValue
Typestring or string[]
RequiredNo
DefaultNone

Additional output directory or directories for all generated files (both API routes and messages). Duck Gen always writes to its internal generated folder inside the package. This option adds extra output locations.

Single directory:

{ "outputSource": "./generated" }

Multiple directories:

{ "outputSource": ["./generated", "../shared-types/generated"] }

When a directory path is given (no file extension), Duck Gen creates files with default names inside that directory. When a file path is given (with .d.ts extension), Duck Gen writes directly to that file.

sourceGlobs

{
  "extensions": {
    "shared": {
      "sourceGlobs": ["src/**/*.ts", "src/**/*.tsx"]
    }
  }
}
PropertyValue
Typestring[]
RequiredNo
StatusReserved, present in the schema but not applied by the current NestJS adapter

The NestJS adapter uses your tsconfig.json include/exclude globs instead. This field is reserved for future adapters that may not use a tsconfig.

API Routes options

The extensions.apiRoutes section configures the API route type generator.

enabled

{
  "extensions": {
    "apiRoutes": {
      "enabled": true
    }
  }
}
PropertyValue
Typeboolean
RequiredYes

Enable or disable API route generation. Set to false if you only want message types.

globalPrefix

{
  "extensions": {
    "apiRoutes": {
      "globalPrefix": "/api"
    }
  }
}
PropertyValue
Typestring
RequiredNo
DefaultNone (empty prefix)

A path prefix applied to all generated routes. This should match the global prefix you set in your NestJS application:

main.ts
const app = await NestFactory.create(AppModule)
app.setGlobalPrefix('api')  // match this in duck-gen.json as "/api"

Without a prefix, routes start directly from the controller path:

  • With globalPrefix: "/api": /api/users/:id
  • Without: /users/:id

normalizeAnyToUnknown

{
  "extensions": {
    "apiRoutes": {
      "normalizeAnyToUnknown": true
    }
  }
}
PropertyValue
Typeboolean
RequiredYes
Recommendedtrue

When enabled:

  • Controller methods with any return types are converted to unknown in generated types.
  • A warning is printed for each method with an any return type.

This prevents any from silently leaking into your client types. Set to false only if you intentionally want any to pass through.

outputSource (apiRoutes)

{
  "extensions": {
    "apiRoutes": {
      "outputSource": "./generated"
    }
  }
}
PropertyValue
Typestring or string[]
RequiredNo

Additional output locations for API route types specifically. Works the same as shared.outputSource but only applies to the API routes file.

This is additive with shared.outputSource. Both locations receive the generated file.

Messages options

The extensions.messages section configures the message registry type generator.

enabled

{
  "extensions": {
    "messages": {
      "enabled": true
    }
  }
}
PropertyValue
Typeboolean
RequiredYes

Enable or disable message generation. Set to false if you only want API route types.

outputSource (messages)

{
  "extensions": {
    "messages": {
      "outputSource": "./generated"
    }
  }
}
PropertyValue
Typestring or string[]
RequiredNo

Additional output locations for message types specifically. Works the same as the API routes outputSource.

Complete example

Here is a fully configured duck-gen.json with every option set:

duck-gen.json
{
  "$schema": "node_modules/@gentleduck/gen/duck-gen.schema.json",
  "framework": "nestjs",
  "extensions": {
    "shared": {
      "includeNodeModules": false,
      "outputSource": ["./generated", "../client/generated"],
      "sourceGlobs": ["src/**/*.ts"],
      "tsconfigPath": "./tsconfig.json"
    },
    "apiRoutes": {
      "enabled": true,
      "globalPrefix": "/api",
      "normalizeAnyToUnknown": true,
      "outputSource": "./generated"
    },
    "messages": {
      "enabled": true,
      "outputSource": "./generated"
    }
  }
}

With this config:

  • API route types are written to:

    1. node_modules/@gentleduck/gen/generated/nestjs/duck-gen-api-routes.d.ts (always)
    2. ./generated/duck-gen-api-routes.d.ts (from shared.outputSource)
    3. ../client/generated/duck-gen-api-routes.d.ts (from shared.outputSource)
    4. ./generated/duck-gen-api-routes.d.ts (from apiRoutes.outputSource, deduped)
  • Message types are written to the same locations but as duck-gen-messages.d.ts.

Configuration examples

API routes only

Use this when your project doesn't use i18n or message keys:

duck-gen.json
{
  "$schema": "node_modules/@gentleduck/gen/duck-gen.schema.json",
  "framework": "nestjs",
  "extensions": {
    "shared": {
      "includeNodeModules": false,
      "sourceGlobs": ["src/**/*.ts"],
      "tsconfigPath": "./tsconfig.json"
    },
    "apiRoutes": {
      "enabled": true,
      "globalPrefix": "/api",
      "normalizeAnyToUnknown": true
    },
    "messages": {
      "enabled": false
    }
  }
}

Messages only

Use this when you only need i18n type safety and handle routes differently:

duck-gen.json
{
  "$schema": "node_modules/@gentleduck/gen/duck-gen.schema.json",
  "framework": "nestjs",
  "extensions": {
    "shared": {
      "includeNodeModules": false,
      "sourceGlobs": ["src/**/*.ts"],
      "tsconfigPath": "./tsconfig.json"
    },
    "apiRoutes": {
      "enabled": false
    },
    "messages": {
      "enabled": true,
      "outputSource": "./generated"
    }
  }
}

Monorepo with shared output

When your server and client live in different packages:

apps/server/duck-gen.json
{
  "$schema": "../../node_modules/@gentleduck/gen/duck-gen.schema.json",
  "framework": "nestjs",
  "extensions": {
    "shared": {
      "includeNodeModules": false,
      "outputSource": ["./generated", "../../packages/shared-types/generated"],
      "sourceGlobs": ["src/**/*.ts"],
      "tsconfigPath": "./tsconfig.json"
    },
    "apiRoutes": {
      "enabled": true,
      "globalPrefix": "/api",
      "normalizeAnyToUnknown": true
    },
    "messages": {
      "enabled": true
    }
  }
}

Then in your client package:

import type { ApiRoutes } from '../../packages/shared-types/generated/duck-gen-api-routes'

Next steps