# client fetch

Source: https://apigenie.pl/techdocs/reference/cli-generate_client_fetch

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

## Usage

```sh
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/`:

```ts
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`.

```ts
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`:

```yaml
overlay: 1.0.0
info:
  title: TypeScript names
  version: 1.0.0
actions:
  - target: $.components.schemas.Pet
    update:
      x-apigenie:
        codegen:
          name: Animal
```

```sh
apigenie-generator client fetch openapi.yaml --overlay fetch.overlay.yaml -o out
```

### Limitations

From the [codegen conformance suite](/techdocs/reference/cli-generate):

- 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`](/techdocs/reference/cli-convert). Documents referencing other files must be
  bundled first with [`apigenie bundle`](/techdocs/reference/cli-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

```yaml
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. |
