/openapi-typescript-fetch

A typed fetch client for openapi-typescript

Primary LanguageTypeScriptMIT LicenseMIT

version(scoped) codecov

📘️ openapi-typescript-fetch

A typed fetch client for openapi-typescript

Install

npm install openapi-typescript-fetch

Or

yarn add openapi-typescript-fetch

Features

Supports JSON request and responses

Usage

Generate typescript definition from schema

npx openapi-typescript https://petstore.swagger.io/v2/swagger.json --output petstore.ts

# 🔭 Loading spec from https://petstore.swagger.io/v2/swagger.json…
# 🚀 https://petstore.swagger.io/v2/swagger.json -> petstore.ts [650ms]

Typed fetch client

import 'whatwg-fetch'

import { Fetcher } from 'openapi-typescript-fetch'

import { paths } from './petstore'

// declare fetcher for paths
const fetcher = Fetcher.for<paths>()

// global configuration
fetcher.configure({
  baseUrl: 'https://petstore.swagger.io/v2',
  init: {
    headers: {
      ...
    },
  },
  use: [...] // middlewares
})

// create fetch operations
const findPetsByStatus = fetcher.path('/pet/findByStatus').method('get').create()
const addPet = fetcher.path('/pet').method('post').create()

// fetch
const { status, data: pets } = await findPetsByStatus({
  status: ['available', 'pending'],
})

console.log(pets[0])

Typed Error Handling

A non-ok fetch response throws a generic ApiError

But an Openapi document can declare a different response type for each status code, or a default error response type

These can be accessed via a discriminated union on status, as in code snippet below

const findPetsByStatus = fetcher.path('/pet/findByStatus').method('get').create()
const addPet = fetcher.path('/pet').method('post').create()

try {
  await findPetsByStatus({ ... })
  await addPet({ ... })
} catch(e) {
  // check which operation threw the exception
  if (e instanceof addPet.Error) {
    // get discriminated union { status, data } 
    const error = e.getActualType()
    if (error.status === 400) {
      error.data.validationErrors // only available for a 400 response
    } else if (error.status === 500) {
      error.data.errorMessage // only available for a 500 response
    } else {
      ...
    }
  }
}

Middleware

Middlewares can be used to pre and post process fetch operations (log api calls, add auth headers etc)

import { Middleware } from 'openapi-typescript-fetch'

const logger: Middleware = async (url, init, next) => {
  console.log(`fetching ${url}`)
  const response = await next(url, init)
  console.log(`fetched ${url}`)
  return response
}

fetcher.configure({
  baseUrl: 'https://petstore.swagger.io/v2',
  init: { ... },
  use: [logger],
})

// or

fetcher.use(logger)

Server Side Usage

This library can be used server side if you have a compatible fetch lib (e.g. node-fetch).

npm install cross-fetch

You will then need to inject fetch into the config:

import altFetch, { Headers } from 'node-fetch'
const fetcher = Fetcher.for<paths>()
fetcher.configure({
  fetchProvider: {
    fetch: altFetch as any,
    headers: Headers as any,
  },
  baseUrl: 'https://api.backend.dev',
  init: {
    headers: {
      Authorization: 'Bearer token',
    },
  },
})

If you want/need to use node-fetch there is an issue because it's not type-compatible with native fetch. See discussion and workarounds for it here: apollographql/apollo-link#513 (comment)

Utility Types

  • OpArgType - Infer argument type of an operation
  • OpReturnType - Infer return type of an operation
  • OpErrorType - Infer error type of an operation
  • FetchArgType - Argument type of a typed fetch operation
  • FetchReturnType - Return type of a typed fetch operation
  • FetchErrorType - Error type of a typed fetch operation
  • TypedFetch - Fetch operation type
import { paths, operations } from './petstore'

type Arg = OpArgType<operations['findPetsByStatus']>
type Ret = OpReturnType<operations['findPetsByStatus']>
type Err = OpErrorType<operations['findPetsByStatus']>

type Arg = OpArgType<paths['/pet/findByStatus']['get']>
type Ret = OpReturnType<paths['/pet/findByStatus']['get']>
type Err = OpErrorType<paths['/pet/findByStatus']['get']>

type FindPetsByStatus = TypedFetch<operations['findPetsByStatus']>

const findPetsByStatus = fetcher.path('/pet/findByStatus').method('get').create()

type Arg = FetchArgType<typeof findPetsByStatus>
type Ret = FetchReturnType<typeof findPetsByStatus>
type Err = FetchErrorType<typeof findPetsByStatus>

Utility Methods

  • arrayRequestBody - Helper to merge params when request body is an array see issue
const body = arrayRequestBody([{ item: 1}], { param: 2})

// body type is { item: number }[] & { param: number }

Happy fetching! 👍