> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/wevm/incur/llms.txt
> Use this file to discover all available pages before exploring further.

# Openapi

> Generate CLI commands from OpenAPI specifications

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.

```typescript theme={null}
async function generateCommands(
  spec: OpenAPISpec,
  fetch: FetchHandler,
  options?: { basePath?: string }
): Promise<Map<string, GeneratedCommand>>
```

<ParamField path="spec" type="OpenAPISpec" required>
  OpenAPI 3.x specification object
</ParamField>

<ParamField path="fetch" type="FetchHandler" required>
  Fetch handler function that processes requests
</ParamField>

<ParamField path="options.basePath" type="string">
  Base path to prepend to all endpoint paths
</ParamField>

<ResponseField name="return" type="Promise<Map<string, GeneratedCommand>>">
  Map of command names to generated command entries
</ResponseField>

#### Example

```typescript theme={null}
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`).

```typescript theme={null}
type OpenAPISpec = {
  paths?: {} | undefined
}
```

### FetchHandler

A fetch handler function.

```typescript theme={null}
type FetchHandler = (req: Request) => Response | Promise<Response>
```

<ParamField path="req" type="Request" required>
  Standard Web API Request object
</ParamField>

<ResponseField name="return" type="Response | Promise<Response>">
  Standard Web API Response object
</ResponseField>

### GeneratedCommand

A generated command entry compatible with incur's internal CommandEntry.

```typescript theme={null}
type GeneratedCommand = {
  args?: z.ZodObject<any> | undefined
  description?: string | undefined
  options?: z.ZodObject<any> | undefined
  run: (context: any) => any
}
```

<ResponseField name="args" type="z.ZodObject<any>">
  Zod schema for path parameters
</ResponseField>

<ResponseField name="description" type="string">
  Command description from `summary` or `description` field
</ResponseField>

<ResponseField name="options" type="z.ZodObject<any>">
  Zod schema for query parameters and request body properties
</ResponseField>

<ResponseField name="run" type="(context: any) => any" required>
  Command handler function
</ResponseField>

## How It Works

### Schema Mapping

The generator maps OpenAPI parameters and request bodies to incur command schemas:

**Path parameters** → `args` schema

```yaml theme={null}
parameters:
  - name: id
    in: path
    required: true
    schema:
      type: integer
```

```typescript theme={null}
// Becomes:
args: z.object({ id: z.number() })
```

**Query parameters** → `options` schema

```yaml theme={null}
parameters:
  - name: limit
    in: query
    schema:
      type: integer
```

```typescript theme={null}
// Becomes:
options: z.object({ limit: z.number().optional() })
```

**Request body** → merged into `options` schema

```yaml theme={null}
requestBody:
  content:
    application/json:
      schema:
        type: object
        properties:
          name:
            type: string
```

```typescript theme={null}
// Becomes:
options: z.object({ name: z.string().optional() })
```

### Command Naming

Commands are named using `operationId` if present, otherwise generated from method and path:

```yaml theme={null}
/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:

```typescript theme={null}
// 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:

```typescript theme={null}
if (!output.ok) {
  return context.error({
    code: `HTTP_${output.status}`,
    message: /* extract from response */
  })
}
```

## Related

* [Fetch](/api/fetch) - Low-level fetch utilities
* [Mounting APIs](/building/mounting-apis) - Guide to mounting REST APIs
* [OpenAPI Integration](/advanced/openapi-integration) - Advanced OpenAPI usage
