---
title: "TypeScript"
description: "The generated TypeScript SDK: readable source, zero runtime dependencies, ESM, and the package the CLI and MCP server are built on."
url: https://typeship.dev/docs/platforms/sdk/typescript
markdown: https://typeship.dev/docs/platforms/sdk/typescript.md
section: "Get started"
---
> ## Documentation index
> Fetch the complete documentation index at https://typeship.dev/llms.txt (every page, one line each) or the full text at https://typeship.dev/llms-full.txt.
> Append .md to any docs URL, or send Accept: text/markdown, for the markdown twin of that page.

# TypeScript

The generated TypeScript SDK: readable source, zero runtime dependencies, ESM, and the package the CLI and MCP server are built on.

The TypeScript SDK is the reference implementation of every feature on the [SDKs overview](https://typeship.dev/docs/platforms/sdk), and the package the other TypeScript platforms import. This page covers what is specific to the TypeScript package.

## Package

* The npm name derives from your API's title. The Acme API produces `acme`. Set a scoped or different name per project under **package names**, for example `@acme/api`.
* Zero runtime dependencies. The only entries in `package.json` are dev dependencies needed to build: `typescript` and `@types/node`.
* ESM only. `"type": "module"`, an `exports` map with an `import` entry, no `require()`. Node 18 or newer, every modern bundler, browsers, and edge runtimes.
* Tree-shakeable: one module per resource and `"sideEffects": false`, so bundlers drop the resources you never call.
* Ships as readable TypeScript source and compiles with `npm run build`. `main`, `types`, and `exports` point into `dist/`, which holds the compiled JavaScript, `.d.ts` declarations, and declaration maps.

## What is exported

```ts
import {
  AcmeClient,          // the client
  environments,        // named servers, when the spec declares two or more
  VERSION,             // the package version
  unwrap,              // ApiResult -> data, or throw the typed error
  NotFoundError,       // one class per documented status
  UnexpectedApiError, TransportError, ValidationError,
  AccountStatus,       // enums as runtime values
  formatDebugEvent,
} from "acme";
import type { Account, AccountsListParams, AccountsListError, ApiResult, RequestOptions } from "acme";
```

Every schema is a type. Every operation has a `<Resource><Method>Params` type when it takes parameters and a `<Resource><Method>Error` union.

## Environment variables

The client reads two, and neither is a credential:

| Variable           | Effect                                                |
| ------------------ | ----------------------------------------------------- |
| `ACME_DEBUG=1`     | Turns on debug logging when `debug` is not set.       |
| `ACME_WEBHOOK_KEY` | The webhook signing key when `webhookKey` is not set. |

Credentials are passed explicitly. That keeps the client safe in browsers and edge runtimes, where `process.env` may not exist. The generated CLI and MCP server are the pieces that read `ACME_TOKEN` and `ACME_BASE_URL`.

## Idioms

* **Nothing throws on HTTP errors.** Every call returns `ApiResult<T, E>` with a typed error union. `unwrap` opts into exceptions. See [Results and errors](https://typeship.dev/docs/platforms/sdk#results-and-errors).
* **`PagePromise`** for list operations: `for await` walks every page, `await` returns one page. See [Pagination](https://typeship.dev/docs/platforms/sdk#pagination).
* **`readOnly` becomes `Omit`.** A referenced schema in a request position appears as `Omit<Account, "id" | "balance">`.
* **Enums are values.** `AccountStatus.FROZEN` exists at runtime and `AccountStatus` is also the literal-union type.
* **Streams are `AsyncIterable<SseEvent>`.** See [Streaming](https://typeship.dev/docs/platforms/sdk#streaming).
* **Webhooks verify with WebCrypto**, so `unwrap` is `async` and the same code runs on Node, browsers, edge runtimes, and Workers.
* **Async by nature.** Every call returns a promise; there is no separate async client because there is nothing else to be.

## Uploads

A `multipart/form-data` field is typed `Blob`. Pass a `File` (a `Blob` with a name, global in browsers and Node 20+) so the part carries a filename; a bare `Blob` is sent under the field name. The other fields of the body travel alongside as form parts, nested values as JSON. A raw binary body (`application/octet-stream`, `image/png`, ...) is a `Blob` too, sent under the spec's content type. `fetch` sets the multipart boundary.

```ts
await client.files.create({ file: new File([bytes], "report.pdf", { type: "application/pdf" }), purpose: "evidence" });
```

## Unions

`oneOf` and `anyOf` become TypeScript unions of the variant types, narrowed the way you narrow any union: on a discriminator property, or with `in` checks. Nothing is wrapped and nothing is guessed at runtime; the payload is the JSON as sent.

## Response metadata

Every `ApiResult`, success or failure, carries `response: { status, headers, requestId }`, so rate-limit headers, a `Location`, or a request id to quote are one property away without going through the `onResponse` hook. Error classes carry the same `response`.

```ts
const result = await client.accounts.get("acct_123");
result.response.headers.get("x-ratelimit-remaining");
result.response.requestId;
```

## GraphQL

A GraphQL schema generates one method per query and mutation field on `client.query` and `client.mutation`. Arguments are the first argument and become variables. Every scalar field to depth 2 is selected by default, with a fragment per concrete type for unions and interfaces, and the optional second argument is a typed selection that replaces it and narrows the result: `client.query.account({ id }, { id: true, owner: { email: true } })`. Unions and interfaces take `on: { TypeName: { ... } }` and come back discriminated by `__typename`. A raw selection-set string is the escape hatch. Connection fields return a `PagePromise` like everything else. Errors in a `200` come back as `GraphQLRequestError` on the error side of the result, carrying the raw `errors` array. See [Generate from GraphQL](https://typeship.dev/docs/guides/graphql).

## Transport

Everything rides on platform `fetch`. Pass `fetch:` to substitute your own for proxies, recording, or tests; connection reuse is whatever the runtime's `fetch` does (Node's undici and every browser pool keep-alive connections by default). Per-call `signal` composes with the per-attempt timeout, so an `AbortController` cancels a request, a paginated walk, or a stream. Streams are read incrementally through the same `fetch`.

## What else is in the package

* `tests/*.test.mjs`: one smoke test per operation on `node:test` and a `node:http` mock. `npm test` builds and runs them. No framework installed, nothing contacted.
* `api.md`: the complete surface reference in one file: every method, parameter table, return type, and error class.
* `AGENTS.md`: context for coding agents.
* With the `cli` platform, `src/cli.ts` and the `acme` bin. With the `mcp` platform, `src/mcp.ts`, the `acme-mcp` bin, `src/worker.ts`, and `wrangler.toml`.

The `files` field publishes `dist/`, `src/`, and `api.md`.

## Sitemap

[Every page of these docs](https://typeship.dev/llms.txt)
