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>>
OpenAPI 3.x specification object
Fetch handler function that processes requests
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>
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
}
Zod schema for path parameters
Command description from summary or description field
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 parameters → args schema
parameters:
- name: id
in: path
required: true
schema:
type: integer
// Becomes:
args: z.object({ id: z.number() })
Query parameters → options 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 */
})
}