mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
Moving documentation to the wiki
This commit is contained in:
parent
d3cc527a2a
commit
ecb3658a94
19
README.md
19
README.md
@ -74,25 +74,8 @@ docker run openapi-typescript-codegen --input sample.yaml --output client
|
||||
|
||||
Documentation
|
||||
===
|
||||
- [Basic usage](docs/basic-usage.md)
|
||||
- [OpenAPI object](docs/openapi-object.md)
|
||||
- [Client instances](docs/client-instances.md) `--name`
|
||||
- [Argument vs. Object style](docs/arguments-vs-object-style.md) `--useOptions`
|
||||
- [Enums vs. Union types](docs/enum-vs-union-types.md) `--useUnionTypes`
|
||||
- [Runtime schemas](docs/runtime-schemas.md) `--exportSchemas`
|
||||
- [Enum with custom names and descriptions](docs/custom-enums.md)
|
||||
- [Nullable props (OpenAPI v2)](docs/nullable-props.md)
|
||||
- [Authorization](docs/authorization.md)
|
||||
- [External references](docs/external-references.md)
|
||||
- [Canceling requests](docs/canceling-requests.md)
|
||||
- [Custom request file](docs/custom-request-file.md)
|
||||
|
||||
Support
|
||||
===
|
||||
- [Babel support](docs/babel-support.md)
|
||||
- [Axios support](docs/axios-support.md)
|
||||
- [Angular support](docs/angular-support.md)
|
||||
- [Node-Fetch support](docs/node-fetch-support.md)
|
||||
The main documentation can be found in the [openapi-typescript-codegen/wiki](https://github.com/ferdikoomen/openapi-typescript-codegen/wiki)
|
||||
|
||||
[npm-url]: https://npmjs.org/package/openapi-typescript-codegen
|
||||
[npm-image]: https://img.shields.io/npm/v/openapi-typescript-codegen.svg
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
# Angular support
|
||||
|
||||
This tool allows you to generate a client based on the Angular [`HttpClient`](https://angular.io/guide/http).
|
||||
The generated services are fully injectable and make use of the [RxJS](https://rxjs.dev/) Observer pattern.
|
||||
If you want to generate the Angular based client then you can specify `--client angular` in the openapi call:
|
||||
|
||||
`openapi --input ./spec.json --output ./generated --client angular`
|
||||
|
||||
The Angular client has been tested with the following versions:
|
||||
|
||||
```
|
||||
"@angular/common": "17.0.x",
|
||||
"@angular/core": "17.0.x",
|
||||
"rxjs": "7.8.x",
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
In the AppModule you can import the services and add them to the list of injectable services:
|
||||
|
||||
```typescript
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { OrganizationService } from './generated/services/OrganizationService';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HttpClientModule,
|
||||
],
|
||||
providers: [
|
||||
OrganizationService,
|
||||
],
|
||||
bootstrap: [
|
||||
AppComponent,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
```
|
||||
|
||||
Inside the component you can inject the service and just use it as you would with any observable:
|
||||
|
||||
```typescript
|
||||
import { Component } from '@angular/core';
|
||||
import { throwError} from 'rxjs';
|
||||
import { catchError, map, retry } from 'rxjs/operators';
|
||||
|
||||
import type { OrganizationService } from './generated/services/OrganizationService';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: `<div>Angular is ready</div>`,
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(private readonly organizationService: OrganizationService) {
|
||||
|
||||
// Supports making a simple call
|
||||
this.organizationService
|
||||
.createOrganization({
|
||||
name: 'OrgName',
|
||||
description: 'OrgDescription',
|
||||
})
|
||||
.subscribe(organization => {
|
||||
console.log(organization);
|
||||
});
|
||||
|
||||
// Or creating flows with rety(), catchError() and map()
|
||||
this.organizationService
|
||||
.getOrganizations()
|
||||
.pipe(
|
||||
retry(3),
|
||||
catchError(error =>
|
||||
throwError(error)
|
||||
),
|
||||
map(organizations => organizations[0]),
|
||||
)
|
||||
.subscribe(organization => {
|
||||
console.log(organization);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -1,37 +0,0 @@
|
||||
# Arguments vs. Object style
|
||||
|
||||
**Flag:** `--useOptions`
|
||||
|
||||
There's no [named parameter](https://en.wikipedia.org/wiki/Named_parameter) in JavaScript or TypeScript, because of
|
||||
that, we offer the flag `--useOptions` to generate code in two different styles.
|
||||
|
||||
**Argument style:**
|
||||
|
||||
```typescript
|
||||
const createUser = (name: string, password: string, type?: string, address?: string) => {
|
||||
// ...
|
||||
};
|
||||
|
||||
// Usage
|
||||
createUser('Jack', '123456', undefined, 'NY US');
|
||||
```
|
||||
|
||||
**Object style:**
|
||||
|
||||
```typescript
|
||||
const createUser = ({ name, password, type, address }: {
|
||||
name: string,
|
||||
password: string,
|
||||
type?: string
|
||||
address?: string
|
||||
}) => {
|
||||
// ...
|
||||
};
|
||||
|
||||
// Usage
|
||||
createUser({
|
||||
name: 'Jack',
|
||||
password: '123456',
|
||||
address: 'NY US'
|
||||
});
|
||||
```
|
||||
@ -1,24 +0,0 @@
|
||||
# Authorization
|
||||
|
||||
The OpenAPI generator supports Bearer Token authorization. In order to enable the sending
|
||||
of tokens in each request you can set the token using the global OpenAPI configuration:
|
||||
|
||||
```typescript
|
||||
import { OpenAPI } from './generated';
|
||||
|
||||
OpenAPI.TOKEN = 'some-bearer-token';
|
||||
```
|
||||
|
||||
Alternatively, we also support an async method that provides the token for each request.
|
||||
You can simply assign this method to the same `TOKEN `property in the global OpenAPI object.
|
||||
|
||||
```typescript
|
||||
import { OpenAPI } from './generated';
|
||||
|
||||
const getToken = async () => {
|
||||
// Some code that requests a token...
|
||||
return 'SOME_TOKEN';
|
||||
};
|
||||
|
||||
OpenAPI.TOKEN = getToken;
|
||||
```
|
||||
@ -1,74 +0,0 @@
|
||||
# Axios support
|
||||
|
||||
This tool allows you to generate a client based on the [`Axios`](https://www.npmjs.com/package/axios) client.
|
||||
The advantage of the Axios client is that it works in both Node.js and Browser based environments.
|
||||
If you want to generate the Axios based client then you can specify `--client axios` in the openapi call:
|
||||
|
||||
`openapi --input ./spec.json --output ./generated --client axios`
|
||||
|
||||
The only downside is that this client needs some additional dependencies to work (due to the missing FormData
|
||||
classes in Node.js).
|
||||
|
||||
```
|
||||
npm install axios --save-dev
|
||||
npm install form-data@4.x --save-dev
|
||||
```
|
||||
|
||||
In order to compile the project and resolve the imports, you will need to add the following properties
|
||||
in your `tsconfig.json` file:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["...", "dom"],
|
||||
"allowSyntheticDefaultImports": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Using a custom Axios client
|
||||
|
||||
Sometime you may want to use your own Axios client created by `axios.create` for advanced configuration (e.g. Usage of the popular [axios-retry](https://github.com/softonic/axios-retry) interceptor) without having to [reimplement](./custom-request-file.md) the entire generated Axios request function.
|
||||
|
||||
In those cases, simply construct your own HttpRequest wrapper implementation and pass it into your API client
|
||||
|
||||
## Example
|
||||
|
||||
Create a file that looks like this, that references file from the `/core` folder of the generated client.
|
||||
|
||||
```typescript
|
||||
|
||||
import axios from 'axios';
|
||||
import axiosRetry from 'axios-retry';
|
||||
import { request as __request } from './request';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import { BaseHttpRequest } from './BaseHttpRequest';
|
||||
import { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { OpenAPIConfig } from './OpenAPI';
|
||||
|
||||
|
||||
export class AxiosHttpRequestWithRetry extends BaseHttpRequest {
|
||||
axiosInstance = axios.create();
|
||||
|
||||
constructor(config: OpenAPIConfig) {
|
||||
super(config);
|
||||
axiosRetry(this.axiosInstance);
|
||||
}
|
||||
|
||||
public override request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return __request(this.config, options, this.axiosInstance);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Then, when instantiating your generated test client, pass in your custom request wrapper class:
|
||||
|
||||
```typescript
|
||||
|
||||
import { AxiosHttpRequestWithRetry } from './AxiosRequestWithRetry';
|
||||
import { GeneratedClient } from './generated/client';
|
||||
|
||||
const client = new GeneratedClient({ BASE: 'http://localhost:8123' }, AxiosHttpRequestWithRetry)
|
||||
|
||||
```
|
||||
@ -1,19 +0,0 @@
|
||||
# Babel support
|
||||
|
||||
If you use enums inside your models / definitions then those enums are by default inside a namespace with the same name
|
||||
as your model. This is called declaration merging. However, the [@babel/plugin-transform-typescript](https://babeljs.io/docs/en/babel-plugin-transform-typescript)
|
||||
does not support these namespaces, so if you are using babel in your project please use the `--useUnionTypes` flag
|
||||
to generate union types instead of traditional enums. More info can be found here: [Enums vs. Union Types](#enums-vs-union-types---useuniontypes).
|
||||
|
||||
**Note:** If you are using Babel 7 and Typescript 3.8 (or higher) then you should enable the `onlyRemoveTypeImports` to
|
||||
ignore any 'type only' imports, see https://babeljs.io/docs/en/babel-preset-typescript#onlyremovetypeimports for more info
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@babel/preset-typescript', {
|
||||
onlyRemoveTypeImports: true,
|
||||
}],
|
||||
],
|
||||
};
|
||||
```
|
||||
@ -1,62 +0,0 @@
|
||||
# Basic usage
|
||||
|
||||
```
|
||||
$ openapi --help
|
||||
|
||||
Usage: openapi [options]
|
||||
|
||||
Options:
|
||||
-V, --version output the version number
|
||||
-i, --input <value> OpenAPI specification, can be a path, url or string content (required)
|
||||
-o, --output <value> Output directory (required)
|
||||
-c, --client <value> HTTP client to generate [fetch, xhr, node, axios, angular] (default: "fetch")
|
||||
--name <value> Custom client class name
|
||||
--useOptions Use options instead of arguments
|
||||
--useUnionTypes Use union types instead of enums
|
||||
--exportCore <value> Write core files to disk (default: true)
|
||||
--exportServices <value> Write services to disk (default: true)
|
||||
--exportModels <value> Write models to disk (default: true)
|
||||
--exportSchemas <value> Write schemas to disk (default: false)
|
||||
--indent <value> Indentation options [4, 2, tab] (default: "4")
|
||||
--postfixServices Service name postfix (default: "Service")
|
||||
--postfixModels Model name postfix
|
||||
--request <value> Path to custom request file
|
||||
-h, --help display help for command
|
||||
|
||||
Examples
|
||||
$ openapi --input ./spec.json --output ./generated
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
**package.json**
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"generate": "openapi --input ./spec.json --output ./generated"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**NPX**
|
||||
|
||||
```
|
||||
npx openapi-typescript-codegen --input ./spec.json --output ./generated
|
||||
```
|
||||
|
||||
**Node.js**
|
||||
|
||||
```javascript
|
||||
const OpenAPI = require('openapi-typescript-codegen');
|
||||
|
||||
OpenAPI.generate({
|
||||
input: './spec.json',
|
||||
output: './generated',
|
||||
});
|
||||
|
||||
// Or by providing the content of the spec directly 🚀
|
||||
OpenAPI.generate({
|
||||
input: require('./spec.json'),
|
||||
output: './generated',
|
||||
});
|
||||
```
|
||||
@ -1,42 +0,0 @@
|
||||
# Canceling requests
|
||||
|
||||
The generated clients support canceling of requests, this works by canceling the promise that
|
||||
is returned from the request. Each method inside a service (operation) returns a `CancelablePromise`
|
||||
object. This promise can be canceled by calling the `cancel()` method.
|
||||
|
||||
Below is an example of canceling the request after a certain timeout:
|
||||
|
||||
```typescript
|
||||
import { UserService } from './myClient';
|
||||
|
||||
const getAllUsers = async () => {
|
||||
|
||||
const request = UserService.getAllUsers();
|
||||
|
||||
setTimeout(() => {
|
||||
if (!request.isResolved() && !request.isRejected()) {
|
||||
console.warn('Canceling request due to timeout');
|
||||
request.cancel();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
await request;
|
||||
};
|
||||
```
|
||||
|
||||
The API of the `CancelablePromise` is similar to a regular `Promise`, but it adds the
|
||||
`cancel()` method and some additional properties:
|
||||
|
||||
```typescript
|
||||
interface CancelablePromise<TResult> extends Promise<TResult> {
|
||||
readonly isResolved: boolean;
|
||||
readonly isRejected: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
cancel: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
- `isResolved`: Indicates if the promise was resolved.
|
||||
- `isRejected`: Indicates if the promise was rejected.
|
||||
- `isCancelled`: Indicates if the promise was canceled.
|
||||
- `cancel()`: Cancels the promise (and request) and throws a rejection error: `Request aborted`.
|
||||
@ -1,27 +0,0 @@
|
||||
# Client instances
|
||||
|
||||
**Flag:** `--name`
|
||||
|
||||
The OpenAPI generator allows creation of client instances to support the multiple backend services use case.
|
||||
The generated client uses an instance of the server configuration and not the global `OpenAPI` constant.
|
||||
To generate a client instance, set a custom name to the client class, use `--name` option.
|
||||
|
||||
```
|
||||
openapi --input ./spec.json --output ./generated --name AppClient
|
||||
```
|
||||
|
||||
The generated client will be exported from the `index` file and can be used as shown below:
|
||||
|
||||
```typescript
|
||||
// Create the client instance with server and authentication details
|
||||
const appClient = new AppClient({
|
||||
BASE: 'http://server-host.com',
|
||||
TOKEN: '1234',
|
||||
});
|
||||
|
||||
// Use the client instance to make the API call
|
||||
const response = await appClient.organizations.createOrganization({
|
||||
name: 'OrgName',
|
||||
description: 'OrgDescription',
|
||||
});
|
||||
```
|
||||
@ -1,47 +0,0 @@
|
||||
# Enum with custom names and descriptions
|
||||
|
||||
You can use `x-enum-varnames` and `x-enum-descriptions` in your spec to generate enum with custom names and descriptions.
|
||||
It's not in official [spec](https://github.com/OAI/OpenAPI-Specification/issues/681) yet. But it's a supported extension
|
||||
that can help developers use more meaningful enumerators.
|
||||
|
||||
```json
|
||||
{
|
||||
"EnumWithStrings": {
|
||||
"description": "This is a simple enum with strings",
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"Success",
|
||||
"Warning",
|
||||
"Error"
|
||||
],
|
||||
"x-enum-descriptions": [
|
||||
"Used when the status of something is successful",
|
||||
"Used when the status of something has a warning",
|
||||
"Used when the status of something has an error"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Generated code:
|
||||
|
||||
```typescript
|
||||
enum EnumWithStrings {
|
||||
/*
|
||||
* Used when the status of something is successful
|
||||
*/
|
||||
Success = 0,
|
||||
/*
|
||||
* Used when the status of something has a warning
|
||||
*/
|
||||
Waring = 1,
|
||||
/*
|
||||
* Used when the status of something has an error
|
||||
*/
|
||||
Error = 2,
|
||||
}
|
||||
```
|
||||
@ -1,61 +0,0 @@
|
||||
# Custom request file
|
||||
|
||||
If you want to implement custom logic on the request level,
|
||||
or implement a client based on a different library, then
|
||||
one option is to write your own request file and tell
|
||||
the generator to use this.
|
||||
|
||||
The request file (`request.ts`) can be found inside the
|
||||
`/core` folder of the generated client. You can modify
|
||||
that file and use it, or alternatively, you can write
|
||||
your own. Below is a very simplified example of an Axios
|
||||
based request file:
|
||||
|
||||
```typescript
|
||||
import axios from 'axios';
|
||||
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OpenAPIConfig } from './OpenAPI';
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
// Your custom Axios instance config
|
||||
});
|
||||
|
||||
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
|
||||
return new CancelablePromise((resolve, reject, onCancel) => {
|
||||
// Get the request URL. Depending on your needs, this might need additional processing,
|
||||
// @see ./src/templates/core/functions/getUrl.hbs
|
||||
const url = `${config.BASE}${options.path}`;
|
||||
|
||||
// Optional: Get and link the cancelation token, so the request can be aborted.
|
||||
const source = axiosInstance.CancelToken.source();
|
||||
onCancel(() => source.cancel('The user aborted a request.'));
|
||||
|
||||
// Execute the request. This is a minimal example, in real world scenarios
|
||||
// you will need to add headers, process form data, etc.
|
||||
// @see ./src/templates/core/axios/request.hbs
|
||||
axiosInstance.request({
|
||||
url,
|
||||
data: options.body,
|
||||
method: options.method,
|
||||
cancelToken: source.token,
|
||||
}).then(data => {
|
||||
resolve(data);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
To use this request file in your generated code you can execute the
|
||||
following command:
|
||||
|
||||
```
|
||||
npx openapi-typescript-codegen --input ./spec.json --output ./generated --request ./request.ts
|
||||
```
|
||||
|
||||
The `--request` parameter will tell the generator to not generate the default
|
||||
`request.ts` file, but instead copy over the custom file that was specified.
|
||||
@ -1,54 +0,0 @@
|
||||
# Enums vs. Union types
|
||||
|
||||
**Flag:** `--useUnionTypes`
|
||||
|
||||
The OpenAPI spec allows you to define [enums](https://swagger.io/docs/specification/data-models/enums/) inside the
|
||||
data model. By default, we convert these enums definitions to [TypeScript enums](https://www.typescriptlang.org/docs/handbook/enums.html).
|
||||
However, these enums are merged inside the namespace of the model, this is unsupported by Babel, [see docs](https://babeljs.io/docs/en/babel-plugin-transform-typescript#impartial-namespace-support).
|
||||
Because we also want to support projects that use Babel [@babel/plugin-transform-typescript](https://babeljs.io/docs/en/babel-plugin-transform-typescript),
|
||||
we offer the flag `--useUnionTypes` to generate [union types](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#union-types)
|
||||
instead of the traditional enums. The difference can be seen below:
|
||||
|
||||
**Enums:**
|
||||
|
||||
```typescript
|
||||
// Model
|
||||
export type Order = {
|
||||
id?: number;
|
||||
quantity?: number;
|
||||
status?: Order.status;
|
||||
};
|
||||
|
||||
export namespace Order {
|
||||
export enum status {
|
||||
PLACED = 'placed',
|
||||
APPROVED = 'approved',
|
||||
DELIVERED = 'delivered',
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const order: Order = {
|
||||
id: 1,
|
||||
quantity: 40,
|
||||
status: Order.status.PLACED,
|
||||
};
|
||||
```
|
||||
|
||||
**Union Types:**
|
||||
|
||||
```typescript
|
||||
// Model
|
||||
export type Order = {
|
||||
id?: number;
|
||||
quantity?: number;
|
||||
status?: 'placed' | 'approved' | 'delivered';
|
||||
};
|
||||
|
||||
// Usage
|
||||
const order: Order = {
|
||||
id: 1,
|
||||
quantity: 40,
|
||||
status: 'placed',
|
||||
};
|
||||
```
|
||||
@ -1,28 +0,0 @@
|
||||
# External references
|
||||
|
||||
Local references to schema definitions (those beginning with `#/definitions/schemas/`)
|
||||
will be converted to type references to the equivalent, generated top-level type.
|
||||
|
||||
The OpenAPI generator also supports external references, which allows you to break
|
||||
down your openapi.yml into multiple sub-files, or incorporate third-party schemas
|
||||
as part of your types to ensure everything is able to be TypeScript generated.
|
||||
|
||||
External references may be:
|
||||
|
||||
* *relative references* - references to other files at the same location e.g.
|
||||
`{ $ref: 'schemas/customer.yml' }`
|
||||
|
||||
* *remote references* - fully qualified references to another remote location e.g.
|
||||
`{ $ref: 'https://myexampledomain.com/schemas/customer_schema.yml' }`
|
||||
|
||||
For remote references, both files (when the file is on the current filesystem)
|
||||
and http(s) URLs are supported.
|
||||
|
||||
External references may also contain internal paths in the external schema (e.g.
|
||||
`schemas/collection.yml#/definitions/schemas/Customer`) and back-references to
|
||||
the base openapi file or between files (so that you can reference another
|
||||
schema in the main file as a type of an object or array property, for example).
|
||||
|
||||
At start-up, an OpenAPI or Swagger file with external references will be "bundled",
|
||||
so that all external references and back-references will be resolved (but local
|
||||
references preserved).
|
||||
@ -1,23 +0,0 @@
|
||||
# Node-Fetch support
|
||||
|
||||
By default, this tool will generate a client that is compatible with the (browser based) Fetch API.
|
||||
However, this client will not work inside the Node.js environment. If you want to generate the Node.js compatible
|
||||
client then you can specify `--client node` in the openapi call:
|
||||
|
||||
`openapi --input ./spec.json --output ./generated --client node`
|
||||
|
||||
This will generate a client that uses [`node-fetch`](https://www.npmjs.com/package/node-fetch) internally. However,
|
||||
in order to compile and run this client, you might need to install the `node-fetch@2.x` dependencies.
|
||||
|
||||
> Since version 3.x [`node-fetch`](https://www.npmjs.com/package/node-fetch) switched to ESM only,
|
||||
> breaking many CommonJS based toolchains (like Jest). Right now we do not support this new version!
|
||||
|
||||
```
|
||||
npm install @types/node-fetch@2.x --save-dev
|
||||
npm install abort-controller@3.x --save-dev
|
||||
npm install form-data@4.x --save-dev
|
||||
npm install node-fetch@2.x --save-dev
|
||||
```
|
||||
|
||||
In order to compile the project and resolve the imports, you will need to enable the `allowSyntheticDefaultImports`
|
||||
in your `tsconfig.json` file.
|
||||
@ -1,38 +0,0 @@
|
||||
# Nullable props (OpenAPI v2)
|
||||
|
||||
In the OpenAPI v3 spec you can create properties that can be `NULL`, by providing a `nullable: true` in your schema.
|
||||
However, the v2 spec does not allow you to do this. You can use the unofficial `x-nullable` in your specification
|
||||
to generate nullable properties in OpenApi v2.
|
||||
|
||||
```json
|
||||
{
|
||||
"ModelWithNullableString": {
|
||||
"required": [
|
||||
"requiredProp"
|
||||
],
|
||||
"description": "This is a model with one string property",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"description": "This is a simple string property",
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
},
|
||||
"requiredProp": {
|
||||
"description": "This is a simple string property",
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Generated code:
|
||||
|
||||
```typescript
|
||||
export type ModelWithNullableString = {
|
||||
prop?: string | null;
|
||||
requiredProp: string | null;
|
||||
};
|
||||
```
|
||||
@ -1,144 +0,0 @@
|
||||
# OpenAPI object
|
||||
|
||||
The library exposes a global OpenAPI object that can be used to configure the requests,
|
||||
below you can find the properties and their usage.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
export const OpenAPI: OpenAPIConfig = {
|
||||
BASE: 'http://localhost:3000/api',
|
||||
VERSION: '2.0',
|
||||
WITH_CREDENTIALS: false,
|
||||
CREDENTIALS: 'include',
|
||||
TOKEN: undefined,
|
||||
USERNAME: undefined,
|
||||
PASSWORD: undefined,
|
||||
HEADERS: undefined,
|
||||
ENCODE_PATH: undefined,
|
||||
};
|
||||
```
|
||||
|
||||
Properties
|
||||
===
|
||||
|
||||
### `OpenAPI.BASE`
|
||||
|
||||
The base path of the OpenAPI server, this is generated from the spec,
|
||||
but can be overwritten to switch servers.
|
||||
|
||||
```typescript
|
||||
if (process.env === 'development') {
|
||||
OpenAPI.BASE = 'http://staging.company.com:3000/api';
|
||||
}
|
||||
if (process.env === 'production') {
|
||||
OpenAPI.BASE = '/api';
|
||||
}
|
||||
```
|
||||
|
||||
### `OpenAPI.VERSION`
|
||||
|
||||
The version param in the OpenAPI paths `{api-version}`. The version is taken from the spec,
|
||||
but can be updated to call multiple versions of the same OpenAPI backend.
|
||||
|
||||
### `OpenAPI.WITH_CREDENTIALS`
|
||||
|
||||
Similar to the [withCredentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials)
|
||||
property of the XHR specification. When set to true, cross-site requests should be made
|
||||
using credentials such as cookies, authorization headers, etc.
|
||||
|
||||
### `OpenAPI.CREDENTIALS`
|
||||
|
||||
Similar to the [credentials](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#sending_a_request_with_credentials_included)
|
||||
property of the Fetch specification. When `OpenAPI.WITH_CREDENTIALS` is set to true,
|
||||
this property controls the specific implementation for Fetch and Node-Fetch clients.
|
||||
Valid values are: `include`, `omit` and `same-origin`.
|
||||
|
||||
### `OpenAPI.TOKEN`
|
||||
|
||||
Set the Bearer authentication token to use for the requests. This needs to be a valid
|
||||
(non-expired) token, otherwise the request will fail. The property can be updated as often
|
||||
as you want, this is useful for scenario's where the token would automatically refresh
|
||||
after x minutes. This property also allows you to use an `async` method that will be resolved
|
||||
before requests are made.
|
||||
|
||||
```typescript
|
||||
OpenAPI.TOKEN = 'MY_TOKEN';
|
||||
|
||||
OpenAPI.TOKEN = async () => {
|
||||
// Note: loading this from a JSON file is not recommended ;-)
|
||||
const response = await fetch('configuration.json');
|
||||
const { token } = response.json();
|
||||
return token;
|
||||
};
|
||||
```
|
||||
|
||||
### `OpenAPI.USERNAME`
|
||||
|
||||
Set the basic authentication username, although not recommended, the basic authentication
|
||||
header is still supported. The username and password hash will be calculated by the client
|
||||
before sending the request. This property also allows you to use an `async` method that
|
||||
will be resolved before requests are made.
|
||||
|
||||
```typescript
|
||||
OpenAPI.USERNAME = 'john';
|
||||
|
||||
OpenAPI.USERNAME = async () => {
|
||||
// Note: loading this from a JSON file is not recommended ;-)
|
||||
const response = await fetch('configuration.json');
|
||||
const { username } = response.json();
|
||||
return username;
|
||||
};
|
||||
```
|
||||
|
||||
### `OpenAPI.PASSWORD`
|
||||
|
||||
Set the basic authentication password. See `OpenAPI.USERNAME` for more info.
|
||||
|
||||
```typescript
|
||||
OpenAPI.PASSWORD = 'welcome123';
|
||||
|
||||
OpenAPI.PASSWORD = async () => {
|
||||
// Note: loading this from a JSON file is not recommended ;-)
|
||||
const response = await fetch('configuration.json');
|
||||
const { password } = response.json();
|
||||
return password;
|
||||
};
|
||||
```
|
||||
|
||||
### `OpenAPI.HEADERS`
|
||||
|
||||
This property allows you to specify additional headers to send for each request. This can be useful
|
||||
for adding headers that are not generated through the spec. Or adding headers for tracking purposes.
|
||||
This property also allows you to use an `async` method that will be resolved before requests are made.
|
||||
|
||||
```typescript
|
||||
OpenAPI.HEADERS = {
|
||||
'x-navigator': window.navigator.appVersion,
|
||||
'x-environment': process.env,
|
||||
'last-modified': 'Wed, 21 Oct 2015 07:28:00 GMT',
|
||||
};
|
||||
|
||||
OpenAPI.HEADERS = async () => {
|
||||
// Note: loading this from a JSON file is not recommended ;-)
|
||||
const response = await fetch('configuration.json');
|
||||
const { headers } = response.json();
|
||||
return headers;
|
||||
};
|
||||
```
|
||||
|
||||
### `OpenAPI.ENCODE_PATH`
|
||||
|
||||
By default, all path parameters are encoded using the `encodeURI` method. This will convert invalid
|
||||
URL characters, for example spaces, backslashes, etc. However, you might want to make the encoding
|
||||
more strict due to security restrictions. So you can set this to `encodeURIComponent` to encode
|
||||
most non-alphanumerical characters to percentage encoding. Or set a customer encoder that just
|
||||
replaces some special characters.
|
||||
|
||||
```typescript
|
||||
OpenAPI.ENCODE_PATH = encodeURIComponent;
|
||||
|
||||
OpenAPI.ENCODE_PATH = (value: string) => {
|
||||
return value.replace(':', '_');
|
||||
};
|
||||
```
|
||||
@ -1,113 +0,0 @@
|
||||
# Runtime schemas
|
||||
|
||||
**Flag:** `--exportSchemas`
|
||||
|
||||
By default, the OpenAPI generator only exports interfaces for your models. These interfaces will help you during
|
||||
development, but will not be available in JavaScript during runtime. However, Swagger allows you to define properties
|
||||
that can be useful during runtime, for instance: `maxLength` of a string or a `pattern` to match, etc. Let's say
|
||||
we have the following model:
|
||||
|
||||
```json
|
||||
{
|
||||
"MyModel": {
|
||||
"required": [
|
||||
"key",
|
||||
"name"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"maxLength": 64,
|
||||
"pattern": "^[a-zA-Z0-9_]*$",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 255,
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"readOnly": true
|
||||
},
|
||||
"modified": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will generate the following interface:
|
||||
|
||||
```typescript
|
||||
export type MyModel = {
|
||||
key: string;
|
||||
name: string;
|
||||
readonly enabled?: boolean;
|
||||
readonly modified?: string;
|
||||
}
|
||||
```
|
||||
|
||||
The interface does not contain any properties like `maxLength` or `pattern`. However, they could be useful
|
||||
if we wanted to create some form where a user could create such a model. In that form you would iterate
|
||||
over the properties to render form fields based on their type and validate the input based on the `maxLength`
|
||||
or `pattern` property. This requires us to have this information somewhere... For this we can use the
|
||||
flag `--exportSchemas` to generate a runtime model next to the normal interface:
|
||||
|
||||
```typescript
|
||||
export const $MyModel = {
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 64,
|
||||
pattern: '^[a-zA-Z0-9_]*$',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 255,
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
isReadOnly: true,
|
||||
},
|
||||
modified: {
|
||||
type: 'string',
|
||||
isReadOnly: true,
|
||||
format: 'date-time',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
```
|
||||
|
||||
These runtime object are prefixed with a `$` character and expose all the interesting attributes of a model
|
||||
and its properties. We can now use this object to generate the form:
|
||||
|
||||
```typescript jsx
|
||||
import { $MyModel } from './generated';
|
||||
|
||||
// Some pseudo code to iterate over the properties and return a form field
|
||||
// the form field could be some abstract component that renders the correct
|
||||
// field type and validation rules based on the given input.
|
||||
const formFields = Object.entries($MyModel.properties)
|
||||
.map(([key, value]) => (
|
||||
<FormField
|
||||
name={key}
|
||||
type={value.type}
|
||||
format={value.format}
|
||||
maxLength={value.maxLength}
|
||||
pattern={value.pattern}
|
||||
isReadOnly={value.isReadOnly}
|
||||
/>
|
||||
));
|
||||
|
||||
const MyForm = () => (
|
||||
<form>
|
||||
{formFields}
|
||||
</form>
|
||||
);
|
||||
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user