{"id":19405816,"url":"https://github.com/vladkens/apigen-ts","last_synced_at":"2025-04-09T15:04:21.030Z","repository":{"id":210232419,"uuid":"725796572","full_name":"vladkens/apigen-ts","owner":"vladkens","description":"Typed, single-file API client generator for OpenAPI schemas with minimal dependencies and maximum type safety.","archived":false,"fork":false,"pushed_at":"2025-04-09T08:35:47.000Z","size":346,"stargazers_count":38,"open_issues_count":2,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-09T15:03:15.246Z","etag":null,"topics":["codegen","http-client","openapi","openapi2","openapi3","openapi31","swagger","typescript"],"latest_commit_sha":null,"homepage":"https://npm.im/apigen-ts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vladkens.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/codeowners","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"vladkens","buy_me_a_coffee":"vladkens"}},"created_at":"2023-11-30T22:25:17.000Z","updated_at":"2025-04-09T08:35:30.000Z","dependencies_parsed_at":"2024-01-09T03:22:15.472Z","dependency_job_id":"a24c4054-9c2f-4044-a43c-c19cc9073e1b","html_url":"https://github.com/vladkens/apigen-ts","commit_stats":null,"previous_names":["vladkens/apigen-ts"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladkens%2Fapigen-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladkens%2Fapigen-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladkens%2Fapigen-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladkens%2Fapigen-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vladkens","download_url":"https://codeload.github.com/vladkens/apigen-ts/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055276,"owners_count":21040156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codegen","http-client","openapi","openapi2","openapi3","openapi31","swagger","typescript"],"created_at":"2024-11-10T11:39:39.406Z","updated_at":"2025-04-09T15:04:21.022Z","avatar_url":"https://github.com/vladkens.png","language":"TypeScript","funding_links":["https://github.com/sponsors/vladkens","https://buymeacoffee.com/vladkens"],"categories":[],"sub_categories":[],"readme":"# apigen-ts\n\n\u003cdiv align=\"center\"\u003e\n\n[\u003cimg src=\"https://badges.ws/npm/v/apigen-ts\" alt=\"version\" /\u003e](https://npmjs.org/package/apigen-ts)\n[\u003cimg src=\"https://badges.ws/packagephobia/publish/apigen-ts\" alt=\"size\" /\u003e](https://packagephobia.now.sh/result?p=apigen-ts)\n[\u003cimg src=\"https://badges.ws/npm/dm/apigen-ts\" alt=\"downloads\" /\u003e](https://npmjs.org/package/apigen-ts)\n[\u003cimg src=\"https://badges.ws/github/license/vladkens/apigen-ts\" alt=\"license\" /\u003e](https://github.com/vladkens/apigen-ts/blob/main/LICENSE)\n[\u003cimg src=\"https://badges.ws/badge/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee\u0026label\" alt=\"donate\" /\u003e](https://buymeacoffee.com/vladkens)\n\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./logo.svg\" alt=\"apigen-ts logo\" height=\"80\" /\u003e\n\u003c/div\u003e\n\n## Features\n\n- Generates ready to use `ApiClient` with types (using `fetch`)\n- Single output file, minimal third-party code\n- Loads schemas from JSON / YAML, locally and remote\n- Ability to customize `fetch` with your custom function\n- Automatic formating with Prettier\n- Can parse dates from date-time format (`--parse-dates` flag)\n- Support OpenAPI v2, v3, v3.1\n- Can be used with npx as well\n\n## Install\n\n```sh\nnpm install apigen-ts --save-dev\n```\n\n```sh\nyarn add -D apigen-ts\n```\n\n## Usage\n\n### 1. Generate\n\n```sh\n# From file\nyarn apigen-ts ./openapi.json ./api-client.ts\n\n# From url\nyarn apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts\n\n# From protected url\nyarn apigen-ts https://secret-api.example.com ./api-client.ts -H \"x-api-key: secret-key\"\n```\n\nRun `yarn apigen-ts --help` for more options. Examples of generated clients [here](./examples/).\n\n### 2. Import\n\n```ts\nimport { ApiClient } from \"./api-client\"\n\nconst api = new ApiClient({\n  baseUrl: \"https://example.com/api\",\n  headers: { Authorization: \"secret-token\" },\n})\n```\n\n### 3. Use\n\n```ts\n// GET /pet/{petId}\nawait api.pet.getPetById(1) // -\u003e Pet\n\n// GET /pet/findByStatus?status=sold\nawait api.pet.findPetsByStatus({ status: \"sold\" }) // -\u003e Pets[]\n\n// PUT /user/{username}; second arg body with type User\nawait api.user.updateUser(\"username\", { firstName: \"John\" })\n```\n\n## Advanced\n\n### Login flow\n\n```ts\nconst { token } = await api.auth.login({ usename, password })\napi.Config.headers = { Authorization: token }\n\nawait api.protectedRoute.get() // here authenticated\n```\n\n### Automatic date parsing\n\n```sh\nyarn apigen-ts ./openapi.json ./api-client.ts --parse-dates\n```\n\n```ts\nconst pet = await api.pet.getPetById(1)\nconst createdAt: Date = pet.createdAt // date parsed from string with format=date-time\n```\n\n### String union as enums\n\nYou can generate string literal union instead of native enums in case you want to run in Node.js environment with [type-stripping](https://nodejs.org/api/typescript.html#type-stripping). To achive this pass `--inline-enums` command line argument or use `inlineEnums: true` in Node.js API.\n\n```sh\nyarn apigen-ts ./openapi.json ./api-client.ts --inline-enums\n```\n\nThis will generate:\n\n```ts\ntype MyEnum = \"OptionA\" | \"OptionB\"\n\n// instead of\nenum MyEnum = {\n  OptionA = \"OptionA\",\n  OptionB = \"OptionB\"\n}\n```\n\n### Errors handling\n\nAn exception will be thrown for all unsuccessful return codes.\n\n```ts\ntry {\n  const pet = await api.pet.getPetById(404)\n} catch (e) {\n  console.log(e) // parse error depend of your domain model, e is awaited response.json()\n}\n```\n\nAlso you can define custom function to parse error:\n\n```ts\nclass MyClient extends ApiClient {\n  async ParseError(rep: Response) {\n    // do what you want\n    return { code: \"API_ERROR\" }\n  }\n}\n\ntry {\n  const api = new MyClient()\n  const pet = await api.pet.getPetById(404)\n} catch (e) {\n  console.log(e) // e is { code: \"API_ERROR\" }\n}\n```\n\n### Base url resolving\n\nYou can modify how the endpoint url is created. By default [URL constructor](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) used to resolve endpoint url like: `new URL(path, baseUrl)` which has specific resolving [rules](https://developer.mozilla.org/en-US/docs/Web/API/URL_API/Resolving_relative_references). E.g.:\n\n- `new URL(\"/v2/cats\", \"https://example.com/v1/\") // -\u003e https://example.com/v2/cats`\n- `new URL(\"v2/cats\", \"https://example.com/v1/\") // -\u003e https://example.com/v1/v2/cats`\n\nIf you want to have custom endpoint url resolving rules, you can override `PrepareFetchUrl` method. For more details see [issue](https://github.com/vladkens/apigen-ts/issues/2).\n\n```ts\nclass MyClient extends ApiClient {\n  PrepareFetchUrl(path: string) {\n    return new URL(`${this.Config.baseUrl}/${path}`.replace(/\\/{2,}/g, \"/\"))\n  }\n}\n\nconst api = new MyClient({ baseUrl: \"https://example.com/v1\" })\n// will call: https://example.com/v1/pet/ instead of https://example.com/pet/\nconst pet = await api.pet.getPetById(404)\n```\n\n### Node.js API\n\nCreate file like `apigen.mjs` with content:\n\n```js\nimport { apigen } from \"apigen-ts\"\n\nawait apigen({\n  source: \"https://petstore3.swagger.io/api/v3/openapi.json\",\n  output: \"./api-client.ts\",\n  // everything below is optional\n  name: \"MyApiClient\", // default \"ApiClient\"\n  parseDates: true, // default false\n  inlineEnums: false, // default false, use string literal union instead of enum\n  headers: { \"x-api-key\": \"secret-key\" }, // Custom HTTP headers to use when fetching schema\n  resolveName(ctx, op, proposal) {\n    // proposal is [string, string] which represents module.funcName\n    if (proposal[0] === \"users\") return // will use default proposal\n\n    const [a, b] = op.name.split(\"/\").slice(3, 5) // eg. /api/v1/store/items/search\n    return [a, `${op.method}_${b}`] // [store, 'get_items'] -\u003e apiClient.store.get_items()\n  },\n})\n```\n\nThen run with: `node apigen.mjs`\n\n## Usage with different backend frameworks\n\n### FastAPI\n\nBy default `apigen-ts` generates noisy method names when used with FastAPI. This can be fixed by [custom resolving](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) for operations ids.\n\n```py\nfrom fastapi import FastAPI\nfrom fastapi.routing import APIRoute\n\napp = FastAPI()\n\n# add your routes here\n\ndef update_operation_ids(app: FastAPI) -\u003e None:\n    for route in app.routes:\n        if isinstance(route, APIRoute):\n            ns = route.tags[0] if route.tags else \"general\"\n            route.operation_id = f\"{ns}_{route.name}\".lower()\n\n\n# this function should be after all routes added\nupdate_operation_ids(app)\n```\n\n_Note: If you want FastAPI to be added as preset, open PR please._\n\n## Compare\n\n- [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) ([npm](https://www.npmjs.com/package/openapi-typescript-codegen)): no single file mode [#1263](https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1263#issuecomment-1502890838)\n- [openapi-typescript](https://github.com/drwpow/openapi-typescript) ([npm](https://www.npmjs.com/package/openapi-typescript)): low level api; no named types export to use in client code\n- [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli) ([npm](https://www.npmjs.com/package/@openapitools/openapi-generator-cli)): wrapper around java lib\n- [swagger-typescript-api](https://github.com/acacode/swagger-typescript-api) ([npm](https://www.npmjs.com/package/swagger-typescript-api)): complicated configuration; user-api breaking changes between versions\n\n## Development\n\n- https://ts-ast-viewer.com\n- https://jsonschemalint.com\n- https://redocly.github.io/redoc/\n- https://swagger.io/docs/specification/basic-structure/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladkens%2Fapigen-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvladkens%2Fapigen-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladkens%2Fapigen-ts/lists"}