OpenAPI SDK generator with strong type guarantees and minimal boilerplate
npm install abaca-cliAn [OpenAPI][] SDK generator with very strong type guarantees and minimal
boilerplate.
1. Install this package as development dependency:
``sh`
npm i -D abaca-cli
2. Use its generate command (or g for short) to create a TypeScript SDK fromnpm
an OpenAPI specification file, typically from an script:
`sh`
abaca g resources/openapi.yaml -o src/sdk.gen.ts
3. Import the generated SDK from your code and instantiate it:
`typescript
import {createSdk} from './sdk.gen.js';
const sdk = createSdk(/ Optional configuration /);
`
4. That's it! The instantiated SDK exposes a strongly typed method for each
operation in OpenAPI specification which you can use to make requests.
`typescript`
const res = await sdk.someOperation(/ Request body, parameters, ... /);
switch (res.code) {
case 200:
doSomething(res.body); // Narrowed response body
break;
// ...
}
Take a look at the repository's README
for more information, examples, and extensions (e.g. Koa integrations).
Abaca checks request types and narrows response types extensively. This section
describes the major components and highlights common patterns.
`typescript`
const res = await sdk.doSomething({
headers: {
'content-type': 'application/json', // 1
accept: 'application/json', // 2
},
params: {/ ... /}, // 3
body: {/ ... /}, // 4
options: {/ ... /}, // 5
});
if (res.code === 200) { // 6
return res.body; // 7
}
1. The content-type header (or, if omitted, the SDK's default) must match oneaccept
of the operation's request body's mime types. The type of the request's body
automatically reflects this value.
2. The header (or, if omitted, the SDK's default) must match one of theparameters
operation's response mime types. The type of the response automatically
reflects this value.
3. Each of the (query, path, and headers) must have the expectedbody
type and be present if required.
4. The request's can only be specified if the operation expects one andcontent-type
must be present if required. Its type must be valid for the operation and
chosen .fetch
5. Request options are type checked against the SDK's local 2XX
implementation.
6. Expected response codes are statically determined from the spec, supporting
both status numbers and ranges (, ...).accept
7. The response's type is automatically narrowed to both the header and
response code.
Abaca automatically type checks each request's body against its 'content-type'application/json
header. In the common case where the header is omitted, the SDK's default is
used (, unless overridden). For example, using theuploadTable operation defined [here][tables], its body should by defaultTable
contain a :
`typescriptTable
await sdk.uploadTable({
headers: {'content-type': 'application/json'}, // Can be omitted
params: {id: 'my-id'},
body: {/ ... /}, // Expected type: `
});
Switching to CSV will automatically change the body's expected type:
`typescriptstring
await sdk.uploadTable({
headers: {'content-type': 'text/csv'}, // Different content-type
params: {id: 'my-id'},
body: '...', // Expected type: `
});
Additionally the 'content-type' header is statically checked to match one of
the defined body types. It also can be auto-completed in compatible editors:
`typescript`
await sdk.uploadTable({
headers: {'content-type': 'application/xml'}, // Compile time error
params: {id: 'my-id'},
});
Abaca automatically narrows the types of responses according to the response's
code and request's 'accept' header. When the header is omitted, it uses theapplication/json;q=1, text/*;q=0.5
SDK's default (similar to request typing above, defaulting to). For example, using the downloadTable
operation defined [here][tables]:
`typescriptTable
const res = await sdk.downloadTable({params: {id: 'my-id'}});
switch (res.code) {
case 200:
res.body; // Narrowed type: undefined
break;
case 404:
res.body; // Narrowed type: `
break;
}
Setting the accept header to CSV updates the response's type accordingly:
`typescriptstring
const res = await sdk.downloadTable({
headers: {accept: 'text/csv'},
params: {id: 'my-id'},
});
switch (res.code) {
case 200:
res.body; // Narrowed type: undefined
break;
case 404:
res.body; // Narrowed type: `
break;
}
Wildcards are also supported. In this case the returned type will be the union
of all possible response values:
`typescriptTable | string
const res = await sdk.downloadTable({
params: {id: 'my-id'},
headers: {accept: '/'},
});
if (res.code === 200) {
res.body; // Narrowed type: `
}
Finally, the accept header itself is type-checked (and auto-completable):
`typescript`
const res = await sdk.downloadTable({
params: {id: 'my-id'},
headers: {
// Valid examples:
accept: 'application/json',
accept: 'application/*',
accept: 'text/csv',
accept: 'text/*',
accept: '/',
// Invalid examples:
accept: 'application/xml',
accept: 'text/plain',
accept: 'image/*',
},
});
The fetch SDK creation option allows swapping the underlying fetchnode-fetch
implementation. SDK method typings will automatically be updated to accept any
additional arguments it supports. For example to use:
`typescript
import fetch from 'node-fetch'
const nodeFetchSdk = createSdk({fetch, /* Other options... /});
await nodeFetchSdk.uploadTable({
options: {
compress: true, // OK: node-fetch argument
},
// ...
});
const fetchSdk = createSdk();
await fetchSdk.uploadTable({
options: {
compress: true, // Type error: default fetch does not support compress`
},
})
`typescript
const sdk = createSdk();
const res = await sdk.runSomeOperation({
params: {/ ... /}, // Checked
body: {/ ... /}, // Checked
headers: {
accept: 'application/json', // Checked (and optional)
'content-type': 'application/json', // Checked (and optional)
},
});
switch (res.code) {
case 200:
res.body; // Narrowed (based on code and accept header)``
// ...
}
[OpenAPI]: https://www.openapis.org/
[tables]: /examples/content-types/resources/openapi.yaml