An open API service indexing awesome lists of open source software.

https://github.com/workos/oagen

A framework for building custom SDK generators from OpenAPI
https://github.com/workos/oagen

Last synced: about 2 months ago
JSON representation

A framework for building custom SDK generators from OpenAPI

Awesome Lists containing this project

README

          

# oagen

oagen is a framework for building custom SDK generators from OpenAPI 3.x specifications.

Its core job is narrow:

- parse an OpenAPI spec into a typed intermediate representation (IR)
- let language emitters turn that IR into files
- regenerate the SDK when the spec changes

More advanced workflows, such as preserving the public API of an existing SDK during a migration to generation, is also supported.

## Who This Is For

oagen is a fit if you:

- need more control than off-the-shelf generators give you
- want to build or maintain a custom emitter for one or more languages
- care about generated output being idiomatic for a specific SDK style

oagen is probably not a fit if you:

- just want a turnkey SDK generator with batteries included
- do not want to maintain emitter code
- do not need a reusable IR or generation framework

## Core Concept

oagen has a small core:

- **Parser**: OpenAPI 3.x -> `ApiSpec` IR
- **Emitter runtime**: `ApiSpec` -> `GeneratedFile[]`
- **Diffing**: compare spec versions and map changes to generated output

Advanced features such as API-surface extraction, compatibility overlays, smoke verification, and live-SDK integration are available, but they are optional. You can ignore them until you need them.

## Quickstart

Install the package:

```bash
npm install @workos/oagen
```

Inspect a spec:

```bash
oagen parse --spec openapi.yml
```

Create an emitter project:

```bash
oagen init --lang ruby --project ./my-emitter
cd ./my-emitter
```

Generate files with your emitter:

```bash
npm run sdk:generate -- --spec ../openapi.yml --namespace MyService
```

For the shortest end-to-end setup, see [Minimal Quickstart](docs/core/quickstart.md).

## The Core API

The default `@workos/oagen` entrypoint is intentionally focused on the framework core:

```ts
import {
defaultSdkBehavior,
mergeSdkBehavior,
diffSpecs,
generate,
generateFiles,
getEmitter,
parseSpec,
planOperation,
registerEmitter,
toCamelCase,
toPascalCase,
toSnakeCase,
} from "@workos/oagen";
import type {
ApiSpec,
SdkBehavior,
Emitter,
EmitterContext,
GeneratedFile,
Model,
Enum,
Service,
OperationPlan,
} from "@workos/oagen";
```

Advanced compat and verification APIs are available through explicit subpaths:

```ts
import {
buildOverlayLookup,
patchOverlay,
registerExtractor,
} from "@workos/oagen/compat";
import { runCompatCheck, runOverlayRetryLoop } from "@workos/oagen/verify";
```

## Building an Emitter

Emitters are pure functions over the IR. They receive typed IR nodes and return `GeneratedFile[]`.

```ts
import type { Emitter } from "@workos/oagen";

const myEmitter: Emitter = {
language: "go",
generateModels: (models, ctx) => [
/* ... */
],
generateEnums: (enums, ctx) => [
/* ... */
],
generateResources: (services, ctx) => [
/* ... */
],
generateClient: (spec, ctx) => [
/* ... */
],
generateErrors: () => [],
generateTests: () => [],
fileHeader: () => "// Auto-generated by oagen. Do not edit.",
};
```

Start with:

- [Reference Emitter](examples/reference-emitter/) — a working TypeScript emitter with tests against a GitHub-flavored fixture spec
- [Minimal Quickstart](docs/core/quickstart.md)
- [Emitter Contract](docs/architecture/emitter-contract.md)
- [IR Type System Reference](docs/architecture/ir-types.md)

## SDK Behavior

`ApiSpec.sdk` contains language-agnostic runtime policies — retry logic, error mapping, telemetry, pagination delays, User-Agent construction, and more. It is always populated (via `defaultSdkBehavior()` during parsing).

Emitters read policy from `ctx.spec.sdk` instead of hardcoding values:

```ts
function generateHttpClient(ctx: EmitterContext) {
const sdk = ctx.spec.sdk;
const retryCodes = sdk.retry.retryableStatusCodes; // [429, 500, 502, 503, 504]
const maxRetries = sdk.retry.maxRetries; // 3
const backoff = sdk.retry.backoff; // { initialDelay: 1, multiplier: 2, maxDelay: 30, jitterFactor: 0.5 }
// ...generate code using these values
}
```

Override defaults per-SDK via `oagen.config.ts`:

```ts
// oagen.config.ts — Python SDK overrides
export default {
sdkBehavior: {
retry: { backoff: { initialDelay: 0.5, maxDelay: 8.0 } },
timeout: {
defaultTimeoutSeconds: 30,
timeoutEnvVar: "WORKOS_REQUEST_TIMEOUT",
},
pagination: { autoPageDelayMs: 0 },
},
};
```

See [`src/ir/sdk-behavior.ts`](src/ir/sdk-behavior.ts) for all interfaces and default values.

## Operation Resolution

`resolveOperations(spec, hints?, mountRules?)` derives method names and mount targets for every operation in the spec. The algorithm produces a snake_case name from the HTTP method and path, then applies optional overrides from a hint map.

Emitters consume `ctx.resolvedOperations` instead of computing names independently, ensuring all SDKs use the same method names (converted to each language's convention).

Configure hints and mount rules in `oagen.config.ts`:

```ts
export default {
operationHints: {
'GET /sso/authorize': { name: 'get_authorization_url' },
'POST /user_management/authenticate': {
split: [
{ name: 'authenticate_with_password', targetVariant: 'PasswordRequest', ... },
],
},
},
mountRules: {
Connections: 'SSO', // All Connections ops mount on SSO
DirectoryGroups: 'DirectorySync',
},
modelHints: {
// Pin a model's placement when "first service to reference it wins"
// would otherwise drift as the spec evolves.
User: 'UserManagementUsers',
},
};
```

Review resolved names with `oagen resolve`:

```bash
oagen resolve --spec openapi.yml --format table # Markdown review table
oagen resolve --spec openapi.yml --format json # JSON for programmatic use
```

See [`src/ir/operation-hints.ts`](src/ir/operation-hints.ts) for types and [`docs/architecture/ir-types.md`](docs/architecture/ir-types.md) for the full reference.

## Commands

| Command | Purpose |
| ---------------- | --------------------------------------------------- |
| `oagen parse` | Parse a spec and print IR JSON |
| `oagen init` | Scaffold an emitter project |
| `oagen generate` | Run a registered emitter |
| `oagen resolve` | Review resolved operation names (table or JSON) |
| `oagen diff` | Compare two specs and output a diff report |
| `oagen extract` | Advanced: extract an SDK API surface for compat use |
| `oagen verify` | Advanced: smoke-test output and run compat checks |

See [CLI Reference](docs/cli.md).

## Documentation

- [Docs Overview](docs/index.md)
- [Core Docs](docs/core/index.md)
- [Advanced Docs](docs/advanced/index.md)
- [Contributor Docs](docs/contributor/index.md)
- [Public API Policy](docs/contributor/public-api.md)
- [Versioning and Migration](docs/contributor/versioning.md)

## Advanced Workflows

oagen also includes tooling for a more opinionated migration workflow:

- extract the public API of an existing SDK
- generate a replacement while preserving names and exports
- verify the generated SDK against smoke tests and compatibility checks
- integrate generated files into a live SDK tree

Those workflows are documented separately because they are not required to use the core framework:

- [Workflows](docs/architecture/workflows.md)
- [Extractor Contract](docs/architecture/extractor-contract.md)
- [CLI Reference](docs/cli.md)

## AI / Plugin Tooling

This repo ships with Claude Code plugin assets and skills for scaffold-and-verify workflows. The framework is usable without any agent tooling.

If you need them, start here:

- [Agent Docs](docs/agents/architecture.md)
- [Emitter Agent Docs](docs/agents/emitters.md)
- [Testing Agent Docs](docs/agents/testing.md)

## Development

```bash
npm install
npm run build
npm test
npm run typecheck
```