Summary
Code Generation
orval
is able to generate axios client with appropriate type-signatures (TypeScript) from any valid OpenAPI v3 or Swagger v2 specification, either in yaml
or json
formats.
Usage
Type-safe data fetchers can be generated from an OpenAPI specification using the following command:
orval --input MY_OPENAPI_SPEC.yaml --output my-awesome-generated-types.tsx
This command can be invoked by either:
- Installing
orval
globally and running it in the terminal: npm i -g orval
, or - Adding a
script
to your package.json
like so:
"scripts": {
"start": "webpack-dev-server",
"build": "webpack -p",
+ "generate-fetcher": "orval --input MY_SWAGGER_DOCS.json --output FETCHERS.tsx"
}
Your client can then be generated by running npm run generate-fetcher
. Optionally, we recommend linting/prettifying the output for readability like so:
"scripts": {
"start": "webpack-dev-server",
"build": "webpack -p",
"generate-fetcher": "orval --input MY_SWAGGER_DOCS.json --output FETCHERS.tsx",
+ "postgenerate-fetcher": "prettier FETCHERS.d.tsx --write"
}
Samples
You can find below some samples on codesandbox
Validation of the OpenAPI specification
To enforce the best quality as possible of specification, we have integrated the amazing OpenAPI linter from IBM. We strongly encourage you to setup your custom rules with a .validaterc
file, you can find all useful information about this configuration here.
Import from GitHub
Using an url in input like this orval --input https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml
allows us to create your client from an OpenAPI spec remotely hosted on GitHub. (how is this real life š„ )
To generate components from remote specifications, you'll need to follow the following steps:
-
Visit your GitHub settings.
-
Click Generate New Token and choose the following:
Token Description: (enter anything)
Scopes:
[X] repo
[X] repo:status
[X] repo_deployment
[X] public_repo
[X] repo:invite
-
Click Generate token.
-
Copy the generated string.
-
Open a terminal and run orval --input https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml
, substituting things where necessary.
-
You will be prompted for a token.
-
Paste your token.
-
You will be asked if you'd like to save it for later. This is entirely up to you and completely safe: it is saved in your node_modules
folder and not committed to version control or sent to us or anything: the source code of this whole thing is public so you're safe.
Caveat: Since your token is stored in node_modules
, your token will be removed on each npm install
of orval
.
-
You're done! š
Transforming an Original Spec
In some cases, you might need to augment an existing OpenAPI specification on the fly, for code-generation purposes.
Transfomer example:
module.exports = (inputSchema) => ({
...inputSchema,
paths: Object.entries(schema.paths).reduce(
(mem, [path, pathItem]) => ({
...mem,
[path]: Object.entries(pathItem).reduce(
(pathItemMem, [verb, operation]) => ({
...pathItemMem,
[verb]: {
...fixOperationId(path, verb, operation),
},
}),
{},
),
}),
{},
),
});
Advanced configuration
orval
supports the concept of "schema stitching" in a RESTful ecosystem as well. We are able to tie multiple backends together and generate code using a single configuration file, orval.config.js
To activate this "advanced mode", replace all flags from your orval
call with the config flag: --config orval.config.js
(or any filename that you want).
ā ļø Note: using a config file makes use of all of the options contained therein, and ignores all other CLI flags.
Config File Format
interface RestfulClientConfig {
[backend: string]: {
output?: string | OutputOptions;
input?: string | InputOptions;
};
}
type OverrideInput = {
transformer?: string;
};
interface InputOptions = {
target?: string;
validation?: boolean;
override?: OverrideInput;
};
type OutputMode = 'single' | 'split' | 'tags';
type MockProperties =
| { [key: string]: unknown }
| ((specs: OpenAPIObject) => { [key: string]: unknown });
type OperationOptions = {
transformer?: string;
mutator?: string;
mock?: {
data?: MockProperties;
properties?: MockProperties;
};
};
type OverrideOutput = {
transformer?: string;
mutator?: string;
operations?: { [key: string]: OperationOptions };
mock?: {
properties?: MockProperties;
};
};
interface OutputOptions = {
target?: string;
schemas?: string;
mode?: OutputMode;
mock?: boolean;
override?: OverrideOutput;
};
Config File Example
module.exports = {
'petstore-file': {
input: 'examples/petstore.yaml',
output: 'examples/petstoreFromFileSpecWithConfig.ts',
},
'petstore-file-transfomer': {
output: {
target: 'examples/petstoreFromFileSpecWithTransformer.ts',
schemas: 'examples/model',
mode: 'split',
mock: true,
},
input: {
target: 'examples/petstore.yaml',
transformer: 'examples/transformer-add-version.js',
},
override: {
operations: {
listPets: {
mutator: 'examples/transformer-response-type.js',
mock: {
properties: () => {
return {
id: faker.random.number({ min: 1, max: 9 }),
};
},
},
},
showPetById: {
mock: {
data: () => ({
id: faker.random.number({ min: 1, max: 99 }),
name: faker.name.firstName(),
tag: faker.helpers.randomize([faker.random.word(), undefined]),
}),
},
},
},
mock: {
properties: {
'/tag|name/': 'jon',
},
},
},
},
};
{
"scripts": {
"gen": "orval --config orval.config.js",
"gen-first": "orval --config orval.config.js myFirstBackend"
}
}