Skip to main content
Search...

Chapter 4: Generating Types

Run Duck Gen for the first time and understand the generated output files.

Run the generator

With your controller from the previous chapter in place, run Duck Gen:

bun run generate

You should see:

Config loaded
Processing done

Explore the output

Duck Gen creates two main output files. Let us look at each one.

Route types

Open generated/duck-gen-api-routes.d.ts. You will see something like this:

generated/duck-gen-api-routes.d.ts
export interface DuckgenApiRoutes {
  '/api/users': {
    GET: {
      body: never
      query: {
        page?: number
        limit?: number
      }
      params: never
      headers: never
      response: {
        data: {
          id: string
          name: string
          email: string
          createdAt: string
        }[]
        message: 'USER_CREATED' | 'USER_UPDATED' | 'USER_DELETED' | 'USER_FOUND' | 'USERS_LISTED'
        success: boolean
      }
    }
    POST: {
      body: {
        name: string
        email: string
        password: string
      }
      query: never
      params: never
      headers: never
      response: {
        data: {
          id: string
          name: string
          email: string
          createdAt: string
        }
        message: 'USER_CREATED' | 'USER_UPDATED' | 'USER_DELETED' | 'USER_FOUND' | 'USERS_LISTED'
        success: boolean
      }
    }
  }
  '/api/users/:id': {
    GET: {
      body: never
      query: never
      params: {
        id: string
      }
      headers: never
      response: {
        data: {
          id: string
          name: string
          email: string
          createdAt: string
        }
        message: 'USER_CREATED' | 'USER_UPDATED' | 'USER_DELETED' | 'USER_FOUND' | 'USERS_LISTED'
        success: boolean
      }
    }
    // PUT and DELETE entries follow the same pattern...
  }
}

Key observations:

  • Every route path is a key in the DuckgenApiRoutes interface.
  • Each path contains its supported HTTP methods.
  • Each method has fully resolved body, query, params, headers, and response types.
  • Unused fields are typed as never, meaning TypeScript will prevent you from sending data that the route does not accept.
  • Generics like ApiResponse<UserDto, UserMessage> are fully expanded.

Message types

Open generated/duck-gen-messages.d.ts. If you have not added @duckgen tags yet (we will do that in Chapter 6), this file will be minimal or contain empty registries.

Barrel exports

Duck Gen also creates index.d.ts barrel files that re-export everything:

generated/index.d.ts
export * from './duck-gen-api-routes'
export * from './duck-gen-messages'

The package output

In addition to your ./generated folder, Duck Gen writes the same files inside node_modules/@gentleduck/gen/generated/nestjs/. This is the primary import path:

import type { ApiRoutes, RouteReq, RouteRes } from '@gentleduck/gen/nestjs'

Understanding utility types

Duck Gen ships several utility types that make working with the route map easier. You do not need to read the raw DuckgenApiRoutes interface directly.

ApiRoutes

The full route map type. Useful when you need to iterate over all routes or build generic utilities:

import type { ApiRoutes } from '@gentleduck/gen/nestjs'
 
// ApiRoutes is the DuckgenApiRoutes interface
type AllPaths = keyof ApiRoutes // '/api/users' | '/api/users/:id'

RouteReq<Path, Method?>

Extracts the request shape for a specific route and method. Returns only the non-never fields:

import type { RouteReq } from '@gentleduck/gen/nestjs'
 
type CreateUserReq = RouteReq<'/api/users', 'POST'>
// { body: { name: string; email: string; password: string } }
 
type ListUsersReq = RouteReq<'/api/users', 'GET'>
// { query: { page?: number; limit?: number } }
 
type GetUserReq = RouteReq<'/api/users/:id', 'GET'>
// { params: { id: string } }

RouteRes<Path, Method?>

Extracts the response type:

import type { RouteRes } from '@gentleduck/gen/nestjs'
 
type CreateUserRes = RouteRes<'/api/users', 'POST'>
// { data: UserDto; message: UserMessage; success: boolean }

Regeneration workflow

Every time you change your server code, run duck-gen again:

# One-time generation
bun run generate
 
# Or watch mode for continuous updates during development
bun run generate:watch

The generated files are deterministic. Running Duck Gen twice with the same source code produces identical output. This makes it safe to run in CI/CD pipelines.

Next

Chapter 5: Using Generated Types