- Notifications
You must be signed in to change notification settings - Fork5
Typed HTTP client generator as single file without extra dependencies from OpenAPI schema
License
vladkens/apigen-ts
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
- Generates ready to use
ApiClient
with types (usingfetch
) - Single output file, minimal third-party code
- Loads schemas from JSON / YAML, locally and remote
- Ability to customize
fetch
with your custom function - Automatic formating with Prettier
- Can parse dates from date-time format (
--parse-dates
flag) - Support OpenAPI v2, v3, v3.1
- Can be used with npx as well
npm install apigen-ts --save-dev
yarn add -D apigen-ts
# From urlyarn apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts# From fileyarn apigen-ts ./openapi.json ./api-client.ts
Runyarn apigen-ts --help
for more options. Examples of generated clientshere.
import{ApiClient}from"./api-client"constapi=newApiClient({baseUrl:"https://example.com/api",headers:{Authorization:"secret-token"},})
// GET /pet/{petId}awaitapi.pet.getPetById(1)// -> Pet// GET /pet/findByStatus?status=soldawaitapi.pet.findPetsByStatus({status:"sold"})// -> Pets[]// PUT /user/{username}; second arg body with type Userawaitapi.user.updateUser("username",{firstName:"John"})
const{ token}=awaitapi.auth.login({ usename, password})api.Config.headers={Authorization:token}awaitapi.protectedRoute.get()// here authenticated
yarn apigen-ts ./openapi.json ./api-client.ts --parse-dates
constpet=awaitapi.pet.getPetById(1)constcreatedAt:Date=pet.createdAt// date parsed from string with format=date-time
You can generate string literal union instead of native enums in case you want to run in Node.js environment withtype-stripping. To achive this pass--inline-enums
command line argument or useinlineEnums: true
in Node.js API.
yarn apigen-ts ./openapi.json ./api-client.ts --inline-enums
This will generate:
typeMyEnum="OptionA"|"OptionB"// instead ofenumMyEnum={OptionA="OptionA",OptionB="OptionB"}
An exception will be thrown for all unsuccessful return codes.
try{constpet=awaitapi.pet.getPetById(404)}catch(e){console.log(e)// parse error depend of your domain model, e is awaited response.json()}
Also you can define custom function to parse error:
classMyClientextendsApiClient{asyncParseError(rep:Response){// do what you wantreturn{code:"API_ERROR"}}}try{constapi=newMyClient()constpet=awaitapi.pet.getPetById(404)}catch(e){console.log(e)// e is { code: "API_ERROR" }}
You can modify how the endpoint url is created. By defaultURL constructor used to resolve endpoint url like:new URL(path, baseUrl)
which has specific resolvingrules. E.g.:
new URL("/v2/cats", "https://example.com/v1/") // -> https://example.com/v2/cats
new URL("v2/cats", "https://example.com/v1/") // -> https://example.com/v1/v2/cats
If you want to have custom endpoint url resolving rules, you can overridePrepareFetchUrl
method. For more details seeissue.
classMyClientextendsApiClient{PrepareFetchUrl(path:string){returnnewURL(`${this.Config.baseUrl}/${path}`.replace(/\/{2,}/g,"/"))}}constapi=newMyClient({baseUrl:"https://example.com/v1"})// will call: https://example.com/v1/pet/ instead of https://example.com/pet/constpet=awaitapi.pet.getPetById(404)
Create file likeapigen.mjs
with content:
import{apigen}from"apigen-ts"awaitapigen({source:"https://petstore3.swagger.io/api/v3/openapi.json",output:"./api-client.ts",// everything below is optionalname:"MyApiClient",// default "ApiClient"parseDates:true,// default falseinlineEnums:false,// default false, use string literal union instead of enumresolveName(ctx,op,proposal){// proposal is [string, string] which represents module.funcNameif(proposal[0]==="users")return// will use default proposalconst[a,b]=op.name.split("/").slice(3,5)// eg. /api/v1/store/items/searchreturn[a,`${op.method}_${b}`]// [store, 'get_items'] -> apiClient.store.get_items()},})
Then run with:node apigen.mjs
By defaultapigen-ts
generates noisy method names when used with FastAPI. This can be fixed bycustom resolving for operations ids.
fromfastapiimportFastAPIfromfastapi.routingimportAPIRouteapp=FastAPI()# add your routes heredefupdate_operation_ids(app:FastAPI)->None:forrouteinapp.routes:ifisinstance(route,APIRoute):ns=route.tags[0]ifroute.tagselse"general"route.operation_id=f"{ns}_{route.name}".lower()# this function should be after all routes addedupdate_operation_ids(app)
Note: If you want FastAPI to be added as preset, open PR please.
- openapi-typescript-codegen (npm): no single file mode#1263
- openapi-typescript (npm): low level api; no named types export to use in client code
- openapi-generator-cli (npm): wrapper around java lib
- swagger-typescript-api (npm): complicated configuration; user-api breaking changes between versions
About
Typed HTTP client generator as single file without extra dependencies from OpenAPI schema