duck gen
Core setup and usage for Duck Gen.
Core
What is Duck Gen?
Duck Gen is the type generation tool in the @gentleduck ecosystem. It scans NestJS server code and emits TypeScript definitions that describe API routes and message keys. The goal is simple: keep server contracts and client types aligned without manual sync work.
Duck Gen currently supports NestJS only.
What it generates
- API route types: A map of routes with request and response types.
- Message registry types: Strongly typed i18n dictionaries based on
@duckgenmessage tags.
How it works
- Reads
duck-gen.tomlfrom your project root (or uses defaults). - Builds a TypeScript project using
tsconfigPathandsourceGlobs. - Scans NestJS controllers and
@duckgenmessage arrays. - Emits
.d.tsfiles under@gentleduck/gen/generated/<framework>. - Prints warnings for edge cases like duplicate message groups or
anytypes.
Installation
Prerequisites
- Node 22 or newer.
- A TypeScript project with a valid
tsconfig.json. - NestJS (the only supported framework today).
Install the package
pnpm add -D @gentleduck/genpnpm add -D @gentleduck/genAdd a script (optional)
package.json
{
"scripts": {
"duck-gen": "duck-gen"
}
}package.json
{
"scripts": {
"duck-gen": "duck-gen"
}
}Quick start
Create duck-gen.toml
duck-gen.toml
framework = "nestjs"
[extensions.shared]
sourceGlobs = ["src/**/*.ts", "src/**/*.tsx"]
tsconfigPath = "./tsconfig.json"
includeNodeModules = false
[extensions.apiRoutes]
enabled = true
globalPrefix = "/api"
normalizeAnyToUnknown = true
[extensions.messages]
enabled = trueduck-gen.toml
framework = "nestjs"
[extensions.shared]
sourceGlobs = ["src/**/*.ts", "src/**/*.tsx"]
tsconfigPath = "./tsconfig.json"
includeNodeModules = false
[extensions.apiRoutes]
enabled = true
globalPrefix = "/api"
normalizeAnyToUnknown = true
[extensions.messages]
enabled = trueRun the generator
pnpm exec duck-genpnpm exec duck-genImport generated types
import type { RouteReq, RouteRes } from '@gentleduck/gen/nestjs'import type { RouteReq, RouteRes } from '@gentleduck/gen/nestjs'Configuration
Duck Gen reads duck-gen.toml from your project root. Paths are resolved
relative to the config file.
duck-gen.toml
framework = "nestjs"
[extensions.shared]
sourceGlobs = ["src/**/*.ts", "src/**/*.tsx"]
tsconfigPath = "./tsconfig.json"
includeNodeModules = false
[extensions.apiRoutes]
enabled = true
globalPrefix = "/api"
normalizeAnyToUnknown = true
[extensions.messages]
enabled = trueduck-gen.toml
framework = "nestjs"
[extensions.shared]
sourceGlobs = ["src/**/*.ts", "src/**/*.tsx"]
tsconfigPath = "./tsconfig.json"
includeNodeModules = false
[extensions.apiRoutes]
enabled = true
globalPrefix = "/api"
normalizeAnyToUnknown = true
[extensions.messages]
enabled = trueRoot
framework(string, default:nestjs): The server framework. Unsupported values emit a warning and fall back tonestjs.
extensions.shared
sourceGlobs(string[], default:["src/**/*.ts", "src/**/*.tsx"]): Files to scan.tsconfigPath(string, default:./tsconfig.json): TypeScript config used by ts-morph.includeNodeModules(boolean, default:false): Includenode_modulesin scanning.
extensions.apiRoutes
enabled(boolean, default:true): Enable API route generation.globalPrefix(string, default:/api): Prefix applied to all routes.normalizeAnyToUnknown(boolean, default:true): Convertsanyrequest types tounknown.sourceGlobs(string[]): Overridesextensions.shared.sourceGlobs.tsconfigPath(string): Overridesextensions.shared.tsconfigPath.includeNodeModules(boolean): Overridesextensions.shared.includeNodeModules.
extensions.messages
enabled(boolean, default:true): Enable message generation.sourceGlobs(string[]): Overridesextensions.shared.sourceGlobs.tsconfigPath(string): Overridesextensions.shared.tsconfigPath.includeNodeModules(boolean): Overridesextensions.shared.includeNodeModules.
Output and CLI
Duck Gen writes generated files inside the package:
@gentleduck/gen/generated/nestjs/duck-gen-api-routes.d.ts@gentleduck/gen/generated/nestjs/duck-gen-messages.d.ts@gentleduck/gen/generated/nestjs/index.d.ts
Import the public entrypoint:
import type { ApiRoutes, DuckgenMessageGroup } from '@gentleduck/gen/nestjs'import type { ApiRoutes, DuckgenMessageGroup } from '@gentleduck/gen/nestjs'CLI notes:
- If
duck-gen.tomlis missing, defaults are used. - Warnings are printed for duplicate message groups or
anyreturn types. - Running the generator multiple times is safe; it overwrites the same files.
Troubleshooting
- No routes generated: Make sure your decorators use string literal paths. Dynamic values are skipped.
- Missing types: Check
sourceGlobsandtsconfigPathinduck-gen.toml. - Message keys are just
string: Addas constto message arrays. - Duplicate group warnings: Ensure each
@duckgengroup key is unique. - Any return warnings: Add explicit return types or update the controller
methods to avoid
any.
Extensions
- API Routes: Read the API routes guide.
- Messages: Read the messages guide.
Contributing
Duck Gen lives in the Gentleduck monorepo. Issues, fixes, and documentation
improvements are welcome at https://github.com/gentleeduck/duck-ui.