0.8.0 • Published 8 months ago

typed-api-fetch v0.8.0

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

typed-api-fetch

typed-api-fetch creates a fetch method that mimics the browser native fetch, but with added type inference.

It is primarily focused on using the TypeScript definitions generated by openapi-typescript, a tool that generates TypeScript definitions from an OpenAPI specification.

Example

const fetch = createFetch<paths>({ baseUrl: "https://petstore3.swagger.io" });

const response = await fetch(
  "/pet/{petId}", // path autocomplete
  {
    method: "get", // available methods depending on given path
    parameters: {
      path: { petId: 42 }, // typed path parameter
    },
  }
);

const data = await response.json();

console.log(data.name);
console.log(data.age); // ❌ property 'age' does not exist

Install

npm install typed-api-fetch

Usage

Generate a TypeScript definition

To generate a TypeScript definition, you can use openapi-typescript to parse an OpenAPI specification.

npx openapi-typescript https://petstore3.swagger.io/api/v3/openapi.json --output petstore.ts

# https://petstore3.swagger.io/api/v3/openapi.json → petstore.ts [818ms]

Create a fetch function

With a type definition stored in ./petstore.ts, it is now possible to build a typed fetch client.

import { paths } from "./petstore";
import createFetch from "typed-api-fetch";

const fetch = createFetch<paths>({
  baseUrl: "https://petstore3.swagger.io",
  defaultInit: {
    headers: {
      Accept: "application/json",
    },
  },
});

The builder accepts the following options

NameTypeDefaultDescription
baseUrlstringPrefixed to the path of the fetch method (eg. https://petstore3.swagger.io)
defaultInitobjectDefault options in the generated fetch method
fetchMethodFunctionfetchA fetch method used to call the API, must comply to the global Fetch method definition
parameterSerializationobject{ path: { explode: false, style: "simple" }, query: { explode: false, style: "form"} }an object describing how path and query parameters should be serialized

Call the generated fetch function

const fetch = createFetch<paths>();
const response = await fetch(
  "/pet/{petId}", // path autocomplete
  {
    method: "get", // available methods depending on given path
    parameters: {
      path: { petId: 42 }, // typed path parameter
    },
  }
);

The fetch function takes two arguments, path and options. options has the same properties as the global fetch function, but with a few differences.

NameTypeDefaultDescription
bodyobjectA JSON object that satisfies the API definition
parametersobjectA record with a path and query property. See the example below this table of how to use it
headersHeadersInit or functionEither a valid Header constructor arguement, or a function that takes an object with resolvedPath and returns a valid HeaderInit

Given the path /pet/{petId}, and the parameter object

{
  path: { petId: 42 },
  query: { page: 3 },
}

the resolved path would be /pet/42?page=3.

Infer the response body

An API can declare different response types for each status code. These can be accessed via a discriminated union on either the status or ok property of the response object.

const response = await fetch("/users", { method: "get" });

if (response.ok) {
  const dataOk = await response.json(); // Infered type of HTTP 2XX status codes
}

if (response.status === 404) {
  const data404 = await response.json(); // Infered type on HTTP 404 status responses
}

Utility types

The Operation and Paths are the generated types from openapi-typescript.

NameDescription
FetchOptions<Operation>The options argument for the fetch function from a given Operation
FetchParameters<Operation>The parameters property withing options, containing the path and query property
ResponseBody<Operation, StatusCode>The response body given a specific HTTP StatusCode
ResponseBodyError<Operation>The response body for error responses (HTTP status code 300-599)
ResponseBodySuccess<Operation>The response body for error responses (HTTP status code 200-299)
SubPaths<Paths, Method>The paths given a specified HTTP Method.

Example implmentations

Using the utility types, you can write a custom implementation using the generated fetch function. Below is a function that makes GET requests, and returns an object with { data, error } depending on the response status code.

import createFetch from "typed-api-fetch";
import type {
  ResponseBodySuccess,
  FetchOptions,
  SubPaths,
} from "typed-api-fetch";
import { paths } from "./petstore-openapi3";

const fetch = createFetch<paths>();

export async function fetchGet<
  GetPath extends SubPaths<paths, "get">,
  Operation extends paths[GetPath]["get"]
>(
  path: GetPath,
  options: FetchOptions<Operation>
): Promise<{
  data?: ResponseBodySuccess<Operation>;
  error?: ResponseBodyError<Operation>;
}> {
  const response = await fetch(path, { ...options, method: "get" });

  if (response.ok) {
    return {
      data: (await response.json()) as ResponseBodySuccess<Operation>,
      error: undefined,
    };
  } else {
    return {
      data: undefined,
      error: (await response.json()) as ResponseBodyError<Operation>,
    };
  }
}

Acknowledgment

Inspired by openapi-typescript-fetch

0.8.0

8 months ago

0.6.2

10 months ago

0.7.0

10 months ago

0.6.1

10 months ago

0.6.0

10 months ago

0.5.0

1 year ago

0.4.0

1 year ago

0.3.2

1 year ago

0.3.1

1 year ago

0.3.0

1 year ago

0.2.1

1 year ago

0.2.0

1 year ago

0.1.0

1 year ago