TypeScript
Generate a typed, zero-dependency TypeScript client for your API.
Typeship generates a typed TypeScript client for your API as a focused, zero-dependency npm package. This page covers the TypeScript-specific surface; shared client behavior is on the SDK overview.
CLI and MCP packages are generated and released separately.
Package
- The npm name initially derives from your API’s title. Configure its repository Delivery under Targets → TypeScript SDK → npm package name, for example
parcel-clientfor the fictional Parcel API. The public Delivery field ispackage_name; separate Targets can have different identities. - Zero runtime dependencies. The only entries in
package.jsonare dev dependencies needed to build:typescriptand@types/node. - ESM only.
"type": "module", anexportsmap with animportentry, norequire(). Node 20 or newer, every modern bundler, browsers, and edge runtimes. - Bundler-friendly:
"sideEffects": false. The validation schema table is a separate module the client loads only when you turn onvalidate; bundlers that split dynamic imports (Vite, Rollup, webpack, or esbuild with--splitting) keep it out of your main bundle. - Ships as readable TypeScript source and compiles with
npm run build.main,types, andexportspoint intodist/, which holds the compiled JavaScript,.d.tsdeclarations, and declaration maps.
What is exported
import {
ParcelClient, // the client
environments, // named servers, when the spec declares two or more
VERSION, // the package version
NotFoundError, // one class per status family: 400, 401, 403, 404, 409, 422, 429, 5xx
ApiError, SdkError, UnexpectedApiError, RateLimitError, ServerError, PayloadError, ResponseParseError, TransportError, ValidationError,
ShipmentStatus, // enums as runtime values
formatDebugEvent,
} from "parcel-client";
import type { Shipment, ShipmentsListParams, ShipmentsListError, RequestOptions } from "parcel-client";Every schema is a type. Every operation has a <Resource><Method>Params type when it takes parameters and a <Resource><Method>Error union.
Choose credentials for each endpoint
When your API has more than one security scheme, customers can pass credentials by the names in components.securitySchemes. For example, an API with customer bearer auth and a serviceKey header exposes credentials.customer and credentials.serviceKey in ClientOptions. Token and API-key values can be strings or callbacks; basic-auth values contain username and password.
Each request sends the first complete permitted combination in the Spec. Alternatives are checked in order. If an endpoint requires two schemes together, both must be available; a partial combination sends neither. Public endpoints receive no generated credentials. Callbacks run only for selected credentials, before each attempt. Your API still enforces token validity and permissions.
Convenience options such as bearerToken, basicAuth, and apiKey remain available when they identify one scheme. Use named credentials to distinguish multiple registrations with the same credential type. Explicit defaultHeaders, per-call headers, and onRequest hooks remain under the application's control and can implement custom authentication.
The generated MCP uses this same request routing. Scope requirements stay attached to each named alternative in operation metadata and tool descriptions.
Environment variables
The client reads the following environment variables. API credentials are passed through client options; the webhook key below is a separate signing secret:
| Variable | Effect |
|---|---|
PARCEL_DEBUG=1 | Turns on debug logging when debug is not set. |
PARCEL_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 PARCEL_TOKEN and PARCEL_BASE_URL.
Idioms
- Awaited calls resolve to data or throw. Status family errors (
NotFoundError,RateLimitError, and the rest) extendApiErrorand are thrown whether or not the operation documents the status; transport, parse, GraphQL, and validation failures have their own classes. See Results and errors. PagePromisefor list operations:awaitreturns the firstPage;for awaitwalks every page. Either path throws the typed error if a page fails. See Pagination.- Data keeps its wire names. Body fields, query parameters, and response fields use the names in your Spec, quoted when a name is not a JavaScript identifier:
client.shipments.list({ page_size: 20, "created[gte]": "2026-01-01" }). Path parameters are positional camelCase arguments, and header parameters are camelCase keys such asidempotencyKey. - Directional models stay honest. A component whose request and response contracts differ becomes
ShipmentWriteandShipmentRead; identical contracts keep the cleanShipmentname. - Enums are values. Request enums are closed. Response enums preserve documented autocomplete while accepting
(string & {}), so a new server value does not make a successful response impossible to represent. - Streams are
AsyncIterable<SseEvent>, orAsyncIterableof the parsed event type for the...Streammethod of an operation that streams on request. See Streaming. - Webhooks verify with WebCrypto, so
unwrapisasyncand 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, and an array of files is Blob[], sent as one part per file. Pass a File (a Blob with a name, global in browsers and Node 20+) so the part carries its filename and type; a bare Blob is named after its field. Anything else in a file field, such as a path string or a Node stream, is rejected with a ValidationError before the request is sent; read the file first with new File([await readFile(path)], name). The other fields travel alongside: an object is one JSON part (application/json), and a type the spec's encoding names for a field is used for that part. A raw binary body (application/octet-stream, image/png, ...) is a Blob too, sent under the spec's content type.
A response whose media type is not JSON is typed Blob (audio, a file, an archive) or string (text/*).
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
Awaited list calls return a Page with response: { status, headers, requestId, rawBody }. For other successful calls, use onResponse to inspect HTTP metadata. HTTP and parse errors expose the response on the error. Every SDK error has a stable code, status, requestId, body, and a message with the next step.
const page = await client.shipments.list();
page.response.headers.get("ratelimit-remaining");
page.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.shipment({ id }, { id: true, recipient: { 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. Errors in a 200 throw GraphQLRequestError, an ApiError carrying the raw errors array. See Generate from 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 onnode:testand anode:httpmock.npm testbuilds 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 that call your API through the package.PUBLISHING.md: how to build, publish, and customize the package. The README stays focused on the package's users.- CLI and MCP files are never included in this package. They belong to their own Targets and destination repositories.
The files field publishes dist/, src/, and api.md.