Generate framework-agnostic TypeScript services and models from OpenAPI/Swagger specifications using the native Fetch API.
Runs on Node, browsers, Vite, SvelteKit, Next.js, Bun, Deno, and any TS runtime that ships fetch. No runtime dependencies in the generated code.
- NPM Package: @metaengine/openapi-fetch
- NuGet Package: MetaEngine.TypeScript.OpenApi.Fetch
- Website: metaengine.eu/packages/openapi-fetch
- ✅ Framework-agnostic — works in any TS runtime with
fetch - ✅ Native Fetch API — zero runtime dependencies in generated code
- ✅ Production-ready — Bearer auth, timeouts, retries, custom headers, middleware, all via CLI flags
- ✅ TypeScript — fully typed clients and models
- ✅ Result pattern — opt into
ApiResult<T>for structured error handling without exceptions - ✅ Middleware hooks —
onRequest/onResponse/onErrorcomposable around the request pipeline - ✅ Vite + SvelteKit —
--import-meta-envforimport.meta.envaccess - ✅ Tree-shakeable — separate file per model and per service
npm install --save-dev @metaengine/openapi-fetchOr use directly with npx:
npx @metaengine/openapi-fetch <input> <output>- Node.js 18.0 or later
- .NET 8.0 or later runtime (Download)
- A TS runtime that supports
fetch(Node 18+, all modern browsers, Bun, Deno)
npx @metaengine/openapi-fetch api.yaml ./src/api \
--documentation \
--result-patternnpx @metaengine/openapi-fetch api.yaml ./src/api \
--bearer-auth API_TOKEN \
--timeout 30 \
--retries 3 \
--custom-header X-Tenant-ID=TENANT_ID \
--error-handlingAdd to your package.json:
{
"scripts": {
"generate:api": "metaengine-openapi-fetch api.yaml ./src/api --bearer-auth API_TOKEN --timeout 30 --retries 3"
}
}Then run:
npm run generate:apiVite / SvelteKit (uses import.meta.env):
npx @metaengine/openapi-fetch api.yaml ./src/api \
--import-meta-env \
--base-url-env VITE_API_URLNext.js:
npx @metaengine/openapi-fetch api.yaml ./src/api \
--base-url-env NEXT_PUBLIC_API_URLPlain Node / Bun / Deno:
npx @metaengine/openapi-fetch api.yaml ./src/api \
--base-url-env API_BASE_URL# From URL
npx @metaengine/openapi-fetch https://api.example.com/openapi.json ./src/api
# Filter by OpenAPI tags
npx @metaengine/openapi-fetch api.yaml ./src/api --include-tags users,auth
# Multiple custom headers from env vars
npx @metaengine/openapi-fetch api.yaml ./src/api \
--custom-header X-Tenant-ID=TENANT_ID \
--custom-header X-App-Id=APP_ID| Option | Description | Default |
|---|---|---|
--include-tags <tags> |
Filter by OpenAPI tags (comma-separated, case-insensitive) | - |
--base-url-env <name> |
Environment variable name for base URL | API_BASE_URL |
--service-suffix <suffix> |
Service naming suffix | Api |
--options-threshold <n> |
Parameter count for options object | 4 |
--documentation |
Generate JSDoc comments | false |
--import-meta-env |
Use import.meta.env for env access (Vite, SvelteKit) |
false |
--result-pattern |
Return ApiResult<T> instead of T for structured error handling |
false |
--middleware |
Emit middleware hooks (onRequest, onResponse, onError) in client |
false |
--error-handling |
Smart error handling based on HTTP status semantics | false |
--bearer-auth <env-var-name> |
Bearer token from env var (adds Authorization: Bearer <token>) |
- |
--timeout <seconds> |
Request timeout in seconds for all operations | - |
--retries <max-attempts> |
Enable retries with exponential backoff (status codes 429, 503) | - |
--custom-header <header=envVarName> |
Static header from env var. Repeatable. | - |
--strict-validation |
Strict OpenAPI validation | false |
--date-transformation |
Convert date strings in responses to Date objects |
false |
--clean |
Clean output directory (remove files not in generation) | false |
--verbose |
Enable verbose logging | false |
--type-mapping <slug=target> |
Override TS type for an OpenAPI format. Repeatable. See Type mapping overrides | - |
--help, -h |
Show help message | - |
output/
├── models/ # One file per model
│ ├── user.ts # export interface User { ... }
│ ├── product.ts
│ └── ...
├── api/ # One file per service/tag
│ ├── users.api.ts # All user operations
│ ├── products.api.ts
│ └── ...
├── client.ts # ClientConfig, createClient, getDefaultClient
└── errors.ts # ApiError, error helpers
npx @metaengine/openapi-fetch api.yaml ./src/api --bearer-auth API_TOKENGenerated client.ts reads process.env.API_TOKEN (or import.meta.env.API_TOKEN with --import-meta-env) and adds Authorization: Bearer <token> to every request. Missing env vars produce a clear error message at startup, not a silent 401.
npx @metaengine/openapi-fetch api.yaml ./src/api --timeout 30Uses AbortSignal.timeout(seconds * 1000) and composes correctly with consumer-supplied AbortSignal via AbortSignal.any.
npx @metaengine/openapi-fetch api.yaml ./src/api --retries 5Retries on 429 and 503 only (configurable defaults from the underlying engine). Exponential backoff: base 0.5s, max delay 30s.
npx @metaengine/openapi-fetch api.yaml ./src/api \
--custom-header X-Tenant-ID=TENANT_ID \
--custom-header X-App-Id=APP_IDRepeatable. Each header value comes from a separate env var, validated at startup.
npx @metaengine/openapi-fetch api.yaml ./src/api --result-patternOperations return ApiResult<T> instead of throwing — useful when you want to handle errors as values rather than exceptions:
const result = await usersApi.getUser('123');
if (result.ok) {
console.log(result.data.name);
} else {
console.error(result.error.code, result.error.message);
}npx @metaengine/openapi-fetch api.yaml ./src/api --middlewareGenerated client emits a Middleware interface and accepts an optional middleware array on ClientConfig:
const client = createClient({
baseUrl: 'https://api.example.com',
middleware: [
{
onRequest: (req) => { console.log('→', req.url); return req; },
onResponse: (res) => { console.log('←', res.status); return res; },
onError: (err) => { console.error('✗', err); throw err; },
},
],
});Middleware composes with --bearer-auth, --timeout, and --error-handling.
Use --type-mapping to override the TS type emitted for a given OpenAPI (type, format) pair. Repeatable. Unknown slugs and unknown targets are hard errors.
| Slug | OpenAPI (type, format) |
Default | --type-mapping value |
|---|---|---|---|
int64 |
(integer, int64) |
number |
int64=bigint |
decimal |
(number, decimal) |
number |
decimal=string |
date-time |
(string, date-time) |
Date |
date-time=string |
date |
(string, date) |
Date |
date=string |
npx @metaengine/openapi-fetch api.yaml ./src/api \
--type-mapping int64=bigint \
--type-mapping date-time=stringTry the generator with your own spec at https://www.metaengine.eu/converters.
The NuGet package allows programmatic use in .NET projects. See the website documentation for full C# API reference.
- Issues: GitHub Issues
- Email: info@metaengine.eu
- Website: metaengine.eu
MIT License - see LICENSE file for details.
This is the documentation and issue tracking repository for MetaEngine OpenAPI Fetch. The compiled NPM package is available at @metaengine/openapi-fetch.
Source code is proprietary, but the package is free to use under MIT license.