apigenie-generatorOpenAPI clientsclient fetch

client fetch

Generate a TypeScript client for the Fetch API from an OpenAPI document.

Usage

apigenie-generator client fetch [...OPTIONS] INPUT_FILE

Description

The generator writes TypeScript sources with no runtime dependencies: requests go through the global fetch() (or one you pass), so the client runs in browsers and in Node.js 18 or later. The input is an OpenAPI 3.0, 3.1 or 3.2 document.

File Contents
src/types.ts a type per component schema, the ...Params interface of each operation, and the named types of inline bodies
src/operations.ts an async function per operation
src/metadata.ts DEFAULT_BASE_URL, the AuthCredentials interface and SECURITY_SCHEMES
src/runtime.ts configure(), ApiError, parameter serialization, authentication, body encoding
src/index.ts exports the types, the operations and the public part of the runtime
package.json, tsconfig.json an ES module package built with tsc into dist/ (layout: package only)

With layout: sources, the five files are written directly to the output directory, to be compiled as part of your own project. A client of a shipping API, generated with layout: sources into api/:

import { configure, ApiError, listShipments, getShipment, downloadLabelFile } from "./api/index.js";

configure({
  baseUrl: "https://eu.api.carpetexpress.example/v2",
  auth: {
    apiKey: "ce_live_123", // sent in the X-Api-Key header
    oauth2: () => fetchAccessToken(), // called before every request that needs it
  },
});

try {
  const page = await listShipments({ status: ["in_transit", "delivered"], limit: 20 });
  const shipment = await getShipment(
    { shipmentId: "shp_01J9Z", expand: ["labels"] },
    { signal: AbortSignal.timeout(5000), headers: { "X-Request-Id": "req-1" } },
  );
  const label: Blob = await downloadLabelFile({ labelId: "lbl_1", extension: "pdf" });
} catch (error) {
  if (error instanceof ApiError) {
    console.error(error.status, error.body); // status 0: no response was received
  }
}

Configuration at run time

configure(settings) sets the client-wide settings; settings it doesn’t give keep their values. Every operation function also takes an optional last argument, RequestOptions, with the same settings for that call only, and an AbortSignal.

Setting Meaning
baseUrl Base URL of the API. Defaults to DEFAULT_BASE_URL: the URL of the document’s first server when it is absolute and has no variables, otherwise empty, and a call without a base URL throws an ApiError.
fetch The fetch() implementation; the global one by default.
headers Headers sent with every request.
auth Credentials, by security scheme name (AuthCredentials).
signal RequestOptions only: aborts the request.

Operations

Every operation is an exported async function named after its operationId in camel case (list_shipments becomes listShipments), or after its method and path when it has none (GET /pets/{id} becomes getPetsId); see action_naming. The summary and the description become its JSDoc comment, and deprecated operations are marked @deprecated.

export async function getShipment(params: GetShipmentParams, options?: RequestOptions): Promise<Shipment>
export async function listShipments(params: ListShipmentsParams = {}, options?: RequestOptions): Promise<ShipmentPage>
export async function getAccount(options?: RequestOptions): Promise<Account>
  • Parameters of all locations (path, query, header, cookie) are properties of one ...Params object, under their names in the document (params["If-Match"]). A name used in two locations gets the location as a prefix for the second one (queryId). Accept, Content-Type and Authorization header parameters are left out. When no parameter is required, the object defaults to {}.
  • Serialization follows each parameter’s style and explode: simple, label and matrix path parameters; form, spaceDelimited, pipeDelimited and deepObject query parameters. Values that are undefined or null are not sent; Date values are sent in ISO 8601 form.
  • The request body is the body property of the params (requestBody when a parameter is already named body). An operation with several request media types gets one: JSON first, then application/x-www-form-urlencoded, multipart/form-data, text/*, and other types. JSON bodies are typed by their schema and sent with JSON.stringify; form bodies are objects (or URLSearchParams), multipart bodies FormData, text bodies string, and other media types Blob.
  • The result is the body of the success response (2XX, or default when no success response is documented): its schema’s type for JSON, string for text/*, Blob for other media types. Several success responses give a union (Promise<Shipment | Job>), and a response without content adds undefined; an operation whose responses have no content returns Promise<void>. The Accept header is set from the media type of the response.
  • Security is the operation’s requirements, or the document’s when the operation declares none. Before the request is sent, the first alternative whose credentials are all configured is applied; when none is, the request goes out without credentials.

Types

Component schemas become exported interfaces (plain objects) or type aliases, named in Pascal case (user_profile becomes UserProfile, x-apigenie.codegen.name overrides the name):

Schema TypeScript
object with properties interface; optional properties are marked ?, deprecated ones @deprecated
additionalProperties Record<string, T>, or an index signature next to properties
enum, const union of literals ("available" | "sold", 1 | 2 | 3)
oneOf, anyOf union (Circle | Square)
allOf intersection (Shape & { name?: string })
prefixItems tuple ([number, number])
type: [T, "null"], OpenAPI 3.0 nullable: true T | null
format: binary string Blob
other strings, date-time included string
integer, number number
{} unknown

Recursive schemas refer to themselves (children?: Tree[]). The client doesn’t validate values against their schemas: constraints such as minimum, pattern or if/then/else generate no code.

Authentication

AuthCredentials has an optional property per security scheme of the document. A credential is a string, or a function returning a string or a promise of one, called before every request that needs it (to refresh tokens).

Scheme Sent as
apiKey the header, query parameter or cookie the scheme names
http with scheme: basic Authorization: Basic ..., the credential being user:password
http with scheme: bearer Authorization: Bearer <credential>
other http schemes Authorization: <scheme> <credential>
oauth2, openIdConnect Authorization: Bearer <credential>

The client implements no OAuth 2.0 flow: it sends the access token you supply.

Errors

Every failure is an ApiError (a subclass of Error) with:

Property Value
status the HTTP status, or 0 when no response was received
body the response body: parsed JSON for JSON media types, otherwise text, undefined when empty
response the Response, when there was one
cause the underlying error, e.g. a network error or a SyntaxError of invalid JSON

A response with a status outside 200-299 throws, as do a network failure, a missing base URL or path parameter, and a success response whose JSON can’t be parsed.

Extensions

Extension Effect
x-apigenie.codegen.name name of the type of a component schema
x-apigenie.codegen.action-name name of the function of an operation, when action_naming lists extension
x-defaultMapping defaultMapping of a discriminator in OpenAPI 3.0 and 3.1 documents: the schema of the values whose discriminator value isn’t mapped

The extensions can live in an overlay for this target instead of in the document, e.g. fetch.overlay.yaml:

overlay: 1.0.0
info:
  title: TypeScript names
  version: 1.0.0
actions:
  - target: $.components.schemas.Pet
    update:
      x-apigenie:
        codegen:
          name: Animal
apigenie-generator client fetch openapi.yaml --overlay fetch.overlay.yaml -o out

Limitations

From the codegen conformance suite:

  • Operation-level and path-level servers are ignored: every request goes to the base URL.
  • x-internal is not honoured: such operations are part of the client.
  • A parameter described with content instead of schema is serialized with the form style, not as JSON.
  • One request media type is generated per operation, so an operation accepting both application/merge-patch+json and application/json-patch+json can send only the first.
  • Streaming responses (text/event-stream, application/jsonl) are read as one JSON value; itemSchema is not used.
  • Form-encoded bodies don’t apply the encoding styles of their properties: an object property is sent as [object Object].
  • Callbacks, webhooks and links aren’t generated: they describe requests the server makes, or documentation.
  • Swagger 2.0 documents are not read; convert them with apigenie convert. Documents referencing other files must be bundled first with apigenie bundle.

Options

-c, –config

Path to the configuration file to use. See the “Configuration” section for more information.

–overlay OVERLAY

Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the extensions of the “Extensions” section. Can be repeated: the overlays apply in order, after those listed under commands.generate.client_fetch.overlay in the configuration file (with paths relative to it). Warnings and errors about a value an overlay set point to the overlay.

–strict

Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated) instead of a warning (OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.).

-o, –output

Required. Directory where the client is generated.

–package-name

Name of the npm package in package.json. Defaults to the document’s info.title in lower case with hyphens between words (Carpet Express Shipping API becomes carpet-express-shipping-api), or api-client when the title has no letters or digits.

–package-version

Version of the npm package in package.json. Defaults to the document’s info.version, or 0.0.0 when it is empty.

Configuration

version: '1.0'
commands:
  generate:
    client_fetch:
      layout: sources
      import_extension: ''
      action_naming: [id, extension, url]
Option Type Default Description
layout package or sources package package writes an npm package (package.json, tsconfig.json, sources in src/); sources writes only the sources, directly in the output directory.
import_extension .js or '' .js Extension of relative imports between the generated files: .js for Node.js ES modules (NodeNext resolution), '' for bundlers.
action_naming id, extension, url, or a list of them [id, url] Where function names come from, in order of preference: the operationId, the x-apigenie.codegen.action-name extension, or the method and path.