npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

abaca-cli

v0.17.0

Published

OpenAPI SDK generator with strong type guarantees and minimal boilerplate

Downloads

22

Readme

Abaca NPM version codecov

An OpenAPI SDK generator with very strong type guarantees and minimal boilerplate.

Quickstart

  1. Install this package as development dependency:
npm i -D abaca-cli
  1. Use its generate command (or g for short) to create a TypeScript SDK from an OpenAPI specification file, typically from an npm script:
abaca g resources/openapi.yaml -o src/sdk.gen.ts
  1. Import the generated SDK from your code and instantiate it:
import {createSdk} from './sdk.gen.js';

const sdk = createSdk(/* Optional configuration */);
  1. That's it! The instantiated SDK exposes a strongly typed method for each operation in OpenAPI specification which you can use to make requests.
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).

Type safety

Abaca checks request types and narrows response types extensively. This section describes the major components and highlights common patterns.

Overview

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 one of the operation's request body's mime types. The type of the request's body automatically reflects this value.
  2. The accept header (or, if omitted, the SDK's default) must match one of the operation's response mime types. The type of the response automatically reflects this value.
  3. Each of the parameters (query, path, and headers) must have the expected type and be present if required.
  4. The request's body can only be specified if the operation expects one and must be present if required. Its type must be valid for the operation and chosen content-type.
  5. Request options are type checked against the SDK's local fetch implementation.
  6. Expected response codes are statically determined from the spec, supporting both status numbers and ranges (2XX, ...).
  7. The response's type is automatically narrowed to both the accept header and response code.

Request body type inference

Abaca automatically type checks each request's body against its 'content-type' header. In the common case where the header is omitted, the SDK's default is used (application/json, unless overridden). For example, using the uploadTable operation defined here, its body should by default contain a Table:

await sdk.uploadTable({
  headers: {'content-type': 'application/json'}, // Can be omitted
  params: {id: 'my-id'},
  body: {/* ... */}, // Expected type: `Table`
});

Switching to CSV will automatically change the body's expected type:

await sdk.uploadTable({
  headers: {'content-type': 'text/csv'}, // Different content-type
  params: {id: 'my-id'},
  body: '...', // Expected type: `string`
});

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:

await sdk.uploadTable({
  headers: {'content-type': 'application/xml'}, // Compile time error
  params: {id: 'my-id'},
});

Response type inference

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 the SDK's default (similar to request typing above, defaulting to application/json;q=1, text/*;q=0.5). For example, using the downloadTable operation defined here:

const res = await sdk.downloadTable({params: {id: 'my-id'}});
switch (res.code) {
  case 200:
    res.body; // Narrowed type: `Table`
    break;
  case 404:
    res.body; // Narrowed type: `undefined`
    break;
}

Setting the accept header to CSV updates the response's type accordingly:

const res = await sdk.downloadTable({
  headers: {accept: 'text/csv'},
  params: {id: 'my-id'},
});
switch (res.code) {
  case 200:
    res.body; // Narrowed type: `string`
    break;
  case 404:
    res.body; // Narrowed type: `undefined`
    break;
}

Wildcards are also supported. In this case the returned type will be the union of all possible response values:

const res = await sdk.downloadTable({
  params: {id: 'my-id'},
  headers: {accept: '*/*'},
});
if (res.code === 200) {
  res.body; // Narrowed type: `Table | string`
}

Finally, the accept header itself is type-checked (and auto-completable):

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/*',
  },
});

Custom fetch implementation

The fetch SDK creation option allows swapping the underlying fetch implementation. SDK method typings will automatically be updated to accept any additional arguments it supports. For example to use node-fetch:

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`
  },
})
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)
  // ...
}