Original link: https://editor.leonh.space/2022/openapi-ts-codegen/
The previous article “OpenAPI opens up the front and back ends of the two lines” introduced OpenAPI and the Python web framework FastAPI, which is deeply integrated with OpenAPI. But it’s not very fragrant, it doesn’t bring much convenience, and it can’t save us 30 years of struggle.
Until recently, I saw another set of openapi-typescript-codegen , and I thought it was really good. I wrote an article to praise it.
As the name suggests, openapi-typescript-codegen will read the API specifications defined by OpenAPI and generate a series of TypeScript modules, which can be easily called in front-end projects.
Install
It is installed like a typical JS suite:
$ npm install openapi-typescript-codegen --save-dev
Generate OpenAPI client module
After installation, there will be one more openapi
command that can be called in the project. We use it to generate the TS module of the API:
$ npx openapi --input http://localhost:5000/openapi.json --output ./src/client
The above parameters should be self-evident, and the openapi.json file of that source may be as follows:
{ " openapi ": " 3.0.2 ", " info ": { " title ": " FastAPI ", " version ": " 0.1.0 " }, " servers ": [ { " url ": " http://localhost:5000 " } ], " paths ": { " /items/{item_id} ": { " get ": { " summary ": " Read Item ", " description ": " Read item by id. ", " operationId ": " read_item ", " parameters ": [ { " required ": true , " schema ": { " title ": " Item Id ", " type ": " integer " }, " name ": " item_id ", " in ": " path " } ], " responses ": { " 200 ": { " description ": " Successful Response ", " content ": { " application/json ": { " schema ": { " $ref ": " #/components/schemas/Item " } } } }, " 422 ": { " description ": " Validation Error ", " content ": { " application/json ": { " schema ": { " $ref ": " #/components/schemas/HTTPValidationError " } } } } } }, " delete ": { " summary ": " Delete Item ", " description ": " Delete item by id. ", " operationId ": " delete_item ", " parameters ": [ { " required ": true , " schema ": { " title ": " Item Id ", " type ": " integer " }, " name ": " item_id ", " in ": " path " } ], " responses ": { " 200 ": { " description ": " Successful Response ", " content ": { " application/json ": { " schema ": {} } } }, " 422 ": { " description ": " Validation Error ", " content ": { " application/json ": { " schema ": { " $ref ": " #/components/schemas/HTTPValidationError " } } } } } } } }, " components ": { " schemas ": { " HTTPValidationError ": { " title ": " HTTPValidationError ", " type ": " object ", " properties ": { " detail ": { " title ": " Detail ", " type ": " array ", " items ": { " $ref ": " #/components/schemas/ValidationError " } } } }, " Item ": { " title ": " Item ", " required ": [ " id ", " name ", " quantity " ], " type ": " object ", " properties ": { " id ": { " title ": " Id ", " type ": " integer " }, " name ": { " title ": " Name ", " type ": " string " }, " quantity ": { " title ": " Quantity ", " type ": " integer " } } }, " ValidationError ": { " title ": " ValidationError ", " required ": [ " loc ", " msg ", " type " ], " type ": " object ", " properties ": { " loc ": { " title ": " Location ", " type ": " array ", " items ": { " type ": " string " } }, " msg ": { " title ": " Message ", " type ": " string " }, " type ": { " title ": " Error Type ", " type ": " string " } } } } } }
Taking this openapi.json as an example, the generated program is as follows:
Among them, core/ is a general request module, which is related to the API specification in models/ and services/. Before going into them, let’s see which modules are exposed in index.ts:
/* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ export { ApiError } from ' ./core/ApiError '; export { CancelablePromise , CancelError } from ' ./core/CancelablePromise '; export { OpenAPI } from ' ./core/OpenAPI '; export type { OpenAPIConfig } from ' ./core/OpenAPI '; export type { HTTPValidationError } from ' ./models/HTTPValidationError '; export type { Item } from ' ./models/Item '; export type { ValidationError } from ' ./models/ValidationError '; export { DefaultService } from ' ./services/DefaultService ';
Compared with the information of Swagger UI above, DefaultService
corresponds to the Default block of Swagger UI, and the type definition of Item
also corresponds to the Item schema in Swagger UI!
Using the OpenAPI client module
It is also very simple to use. For example, to request information from “Read Item”:
import { DefaultService } from ' ./client '; let itemId = 102082 ; async function readItem ( () => { const response = await DefaultService . readItem ( itemId ); consle . debug ( response ); })
Benefiting from the complete type definition features of generators and TypeScript, the IDE’s prompting and completion functions are fully utilized, and you can give the information required by the API without looking at the Swagger UI.
Or to delete an Item:
import { DefaultService } from ' ./client '; let itemId = 102002 ; async function deleteItem ( () => { const response = await DefaultService . deleteItem ( itemId ); consle . debug ( response ); })
Whether it is GET, DELETE, POST, PUT, etc., they are all called like this. No more hand-carved xxx interceptors, no need to re-encapsulate it by yourself, and the lazy package given by openapi-typescript-codegen can make We call API gracefully.
Authentication
It is of course possible to attach authentication information. OpenAPI itself supports several security schemes , while openapi-typescript-codegen supports HTTP Basic authentication and OAuth 2 access token authentication.
Take access token as an example, you can use it like this:
import { OpenAPI , DefaultService } from ' ./client '; OpenAPI . TOKEN = ' eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ' let itemId = 102082 ; async function readItem ( () => { const response = await DefaultService . readItem ( itemId ); consle . debug ( response ); })
As long as this line, all the following interactions will automatically attach the token, steaming great!
As for how the access token comes, it depends on the design of the API server. If it is an OAuth 2 password flow, which requires only one interaction, it can also be obtained with the generated client module. If it requires multiple interactions, such as OAuth 2 Implicit flow , authorization code flow, PKCE flow, etc., you have to do it yourself. After all, those complex flows may also require the participation of “front-end back-end”, which cannot be done by a front-end kit alone.
The OpenAPI
introduced above is an object of the OpenAPIConfig
type, which can be used not only to configure the token, but also to configure parameters such as HTTP headers when making a request, depending on the application requirements!
Epilogue
OpenAPI is already a de facto industry standard. Many open data suppliers provide OpenAPI files. With openapi-typescript-codegen, they can speed up the development of the client. If it is its own product, it is even more necessary between the front-end and the back-end. A bridge like OpenAPI establishes a communication channel, unless you like writing documents in Word.
Compared with writing in Word, it is much more efficient to directly convert the code to OpenAPI. The back-end generates OpenAPI files from the code, and the front-end generates programs from the OpenAPI files, just like a smooth-running pipeline, free and full Comfortable.
In addition to Python’s FastAPI, most other frameworks have built-in or external OpenAPI integration, such as APIFlask, Strapi , etc., which can be used more.
This article is reprinted from: https://editor.leonh.space/2022/openapi-ts-codegen/
This site is for inclusion only, and the copyright belongs to the original author.