Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mikestead/openapi-client
Generate ES6 or Typescript service integration code from an OpenAPI 2 spec
https://github.com/mikestead/openapi-client
code-generation javascript openapi swagger typescript
Last synced: 13 days ago
JSON representation
Generate ES6 or Typescript service integration code from an OpenAPI 2 spec
- Host: GitHub
- URL: https://github.com/mikestead/openapi-client
- Owner: mikestead
- License: mit
- Created: 2016-06-14T01:45:54.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2023-04-25T20:21:42.000Z (over 1 year ago)
- Last Synced: 2024-10-27T13:29:38.584Z (19 days ago)
- Topics: code-generation, javascript, openapi, swagger, typescript
- Language: TypeScript
- Homepage:
- Size: 104 KB
- Stars: 93
- Watchers: 10
- Forks: 34
- Open Issues: 21
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# OpenAPI Client
Generate ES6 or Typescript service integration code from an OpenAPI 2.0 spec.
Also supports optional Redux action creator generation.
Tested against JSON services.
## Install
In your project
npm install openapi-client --save-dev
Or globally to run CLI from anywhere
npm install openapi-client -g
### Type Dependencies
If targeting TypeScript you'll also need to install the `isomorphic-fetch` typings in your project.
npm install @types/isomorphic-fetch --save-dev
## Usage – Generating the API client
`openapi-client` generates action creators in the `outDir` of your choosing. The rest of the examples assume that you've set `--outDir api-client`. You can generate the `api-client` either using the CLI, or in code.
### CLI
```
Usage: openapi [options]Options:
-h, --help output usage information
-V, --version output the version number
-s, --src The url or path to the Open API spec file
-o, --outDir The path to the directory where files should be generated
-l, --language The language of code to generate
--redux True if wanting to generate redux action creators
```### Code
```javascript
const openapi = require('openapi-client')
openapi.genCode({
src: 'http://petstore.swagger.io/v2/swagger.json',
outDir: './src/service',
language: 'ts',
redux: true
})
.then(complete, error)function complete(spec) {
console.info('Service generation complete')
}function error(e) {
console.error(e.toString())
}
```## Usage – Integrating into your project
### Initialization
If you don't need authorization, or to override anything provided by your OpenAPI spec, you can use the actions generated by `openapi-client` directly. However, most of the time you'll need to perform some authorization to use your API. If that's the case, you can initialize the client, probably in the `index.js` of your client-side app:
```javascript
import serviceGateway from './path/to/service/gateway';serviceGateway.init({
url: 'https://service.com/api', // set your service url explicitly. Defaults to the one generated from your OpenAPI spec
getAuthorization // Add a `getAuthorization` handler for when a request requires auth credentials
});// The param 'security' represents the security definition in your OpenAPI spec a request is requiring
// For bearer type it has two properties:
// 1. id - the name of the security definition from your OpenAPI spec
// 2. scopes - the token scope(s) required
// Should return a promise
function getAuthorization(security) {
switch (security.id) {
case 'account': return getAccountToken(security);
// case 'api_key': return getApiKey(security); // Or any other securityDefinitions from your OpenAPI spec
default: throw new Error(`Unknown security type '${security.id}'`)
}
};function getAccountToken(security) {
const token = findAccountToken(security); // A utility function elsewhere in your application that returns a string containing your token – possibly from Redux or localStorage
if (token) return Promise.resolve({ token: token.value });
else throw new Error(`Token ${type} ${security.scopes} not available`);
}
```#### Initialization Options
The full set of gateway initialization options.
```TypeScript
export interface ServiceOptions {
/**
* The service url.
*
* If not specified then defaults to the one defined in the Open API
* spec used to generate the service api.
*/
url?: string${ST}
/**
* Fetch options object to apply to each request e.g
*
* { mode: 'cors', credentials: true }
*
* If a headers object is defined it will be merged with any defined in
* a specific request, the latter taking precedence with name collisions.
*/
fetchOptions?: any${ST}
/**
* Function which should resolve rights for a request (e.g auth token) given
* the OpenAPI defined security requirements of the operation to be executed.
*/
getAuthorization?: (security: OperationSecurity,
securityDefinitions: any,
op: OperationInfo) => Promise${ST}
/**
* Given an error response, custom format and return a ServiceError
*/
formatServiceError?: (response: FetchResponse, data: any) => ServiceError${ST}
/**
* Before each Fetch request is dispatched this function will be called if it's defined.
*
* You can use this to augment each request, for example add extra query parameters.
*
* const params = reqInfo.parameters;
* if (params && params.query) {
* params.query.lang = "en"
* }
* return reqInfo
*/
processRequest?: (op: OperationInfo, reqInfo: RequestInfo) => RequestInfo${ST}
/**
* If you need some type of request retry behavior this function
* is the place to do it.
*
* The response is promise based so simply resolve the "res" parameter
* if you're happy with it e.g.
*
* if (!res.error) return Promise.resolve({ res });
*
* Otherwise return a promise which flags a retry.
*
* return Promise.resolve({ res, retry: true })
*
* You can of course do other things before this, like refresh an auth
* token if the error indicated it expired.
*
* The "attempt" param will tell you how many times a retry has been attempted.
*/
processResponse?: (req: api.ServiceRequest,
res: Response,
attempt: number) => Promise${ST}
/**
* If a fetch request fails this function gives you a chance to process
* that error before it's returned up the promise chain to the original caller.
*/
processError?: (req: api.ServiceRequest,
res: api.ResponseOutcome) => Promise${ST}
/**
* By default the authorization header name is "Authorization".
* This property allows you to override it.
*
* One place this can come up is where your API is under the same host as
* a website it powers. If the website has Basic Auth in place then some
* browsers will override your "Authorization: Bearer " header with
* the Basic Auth value when calling your API. To counter this we can change
* the header, e.g.
*
* authorizationHeader = "X-Authorization"
*
* The service must of course accept this alternative.
*/
authorizationHeader?: string${ST}
}
```### Using generated Redux action creators
You can use the generated API client directly. However, if you pass `--redux` or `redux: true` to `openapi-client`, you will have generated Redux action creators to call your API (using a wrapper around `fetch`). The following example assumes that you're using `react-redux` to wrap action creators in `dispatch`. You also need to use for example `redux-thunk` as middleware to allow async actions.
In your component:
```jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import functional from 'react-functional';import { getPetById } from '../api-client/action/pet';
const Pet = ({ actions, pet }) => (
{pet.name}
)// Dispatch an action to get the pet when the component mounts. Here we're using 'react-functional', but this could also be done using the class componentDidMount method
Pet.componentDidMount = ({ actions }) => actions.getPetById(id);const mapStateToProps = state => (
{
pet: getPet(state) // a function that gets
}
);const mapDispatchToProps = dispatch => (
{
actions: bindActionCreators({ getPetById }, dispatch)
}
);export default connect( mapStateToProps, mapDispatchToProps)(functional(Pet));
```The client can't generate your reducer for you as it doesn't know how merge the returned object into state, so you'll need to add a something to your reducer, such as:
```jsx
export default function reducer(state = initialState, action) {
switch (action.type) {
case GET_PET_BY_ID_START:
return state.set('isFetching', true);
case GET_PET_BY_ID: // When we actually have a pet returned
if(!action.error){
return state.merge({
isFetching: false,
pet: action.payload,
error: null,
});
}
else{ // handle an error
return state.merge({
isFetching: false,
error: action.error,
});
}
default:
return state;
}
}
```