Skip to main content
The Openapi module generates incur command entries from OpenAPI 3.x specifications, allowing you to mount REST APIs as CLI commands.

Functions

generateCommands

Generates incur command entries from an OpenAPI spec. Resolves all $ref pointers.
async function generateCommands(
  spec: OpenAPISpec,
  fetch: FetchHandler,
  options?: { basePath?: string }
): Promise<Map<string, GeneratedCommand>>
spec
OpenAPISpec
required
OpenAPI 3.x specification object
fetch
FetchHandler
required
Fetch handler function that processes requests
options.basePath
string
Base path to prepend to all endpoint paths
return
Promise<Map<string, GeneratedCommand>>
Map of command names to generated command entries

Example

import { Cli, Openapi } from 'incur'

// Load OpenAPI spec
const spec = await import('./api-spec.json')

// Create fetch handler
const fetchHandler = async (req: Request) => {
  const url = new URL(req.url)
  url.host = 'api.example.com'
  url.protocol = 'https:'
  return fetch(new Request(url, req))
}

// Generate commands
const commands = await Openapi.generateCommands(
  spec,
  fetchHandler,
  { basePath: '/v1' }
)

// Mount in CLI
const cli = Cli.create({
  name: 'mycli',
  version: '1.0.0',
  commands: Object.fromEntries(commands)
})

Types

OpenAPISpec

A minimal OpenAPI 3.x spec shape. Accepts both hand-written specs and generated ones (e.g., from @hono/zod-openapi).
type OpenAPISpec = {
  paths?: {} | undefined
}

FetchHandler

A fetch handler function.
type FetchHandler = (req: Request) => Response | Promise<Response>
req
Request
required
Standard Web API Request object
return
Response | Promise<Response>
Standard Web API Response object

GeneratedCommand

A generated command entry compatible with incur’s internal CommandEntry.
type GeneratedCommand = {
  args?: z.ZodObject<any> | undefined
  description?: string | undefined
  options?: z.ZodObject<any> | undefined
  run: (context: any) => any
}
args
z.ZodObject<any>
Zod schema for path parameters
description
string
Command description from summary or description field
options
z.ZodObject<any>
Zod schema for query parameters and request body properties
run
(context: any) => any
required
Command handler function

How It Works

Schema Mapping

The generator maps OpenAPI parameters and request bodies to incur command schemas: Path parametersargs schema
parameters:
  - name: id
    in: path
    required: true
    schema:
      type: integer
// Becomes:
args: z.object({ id: z.number() })
Query parametersoptions schema
parameters:
  - name: limit
    in: query
    schema:
      type: integer
// Becomes:
options: z.object({ limit: z.number().optional() })
Request body → merged into options schema
requestBody:
  content:
    application/json:
      schema:
        type: object
        properties:
          name:
            type: string
// Becomes:
options: z.object({ name: z.string().optional() })

Command Naming

Commands are named using operationId if present, otherwise generated from method and path:
/users/{id}:
  get:
    operationId: getUser  # Uses this

/posts:
  post:  # No operationId
    # Generates: post__posts

Type Coercion

CLI arguments are always strings, so the generator adds coercion for numeric and boolean types:
// OpenAPI schema: type: integer
// Generated: z.coerce.number()

// OpenAPI schema: type: boolean  
// Generated: z.coerce.boolean()

Error Handling

The generated handler uses context.error() for non-2xx responses:
if (!output.ok) {
  return context.error({
    code: `HTTP_${output.status}`,
    message: /* extract from response */
  })
}

Build docs developers (and LLMs) love