---
title: "Generate from GraphQL"
description: "A GraphQL schema generates the same package as an OpenAPI spec, on the same runtime: queries on client.query, mutations on client.mutation, typed selections, connections that auto-paginate."
url: https://typeship.dev/docs/guides/graphql
markdown: https://typeship.dev/docs/guides/graphql.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.

# Generate from GraphQL

A GraphQL schema generates the same package as an OpenAPI spec, on the same runtime: queries on client.query, mutations on client.mutation, typed selections, connections that auto-paginate.

typeship accepts a GraphQL schema wherever it accepts an OpenAPI spec. The output is a TypeScript, Python, or Go package with the same client, results, errors, retries, and platforms. The CLI and MCP server are TypeScript, as for every spec.

## Inputs

Three shapes work as a project's spec URL or as an ad hoc input:

* **SDL**: a `.graphql` schema file.
* **Introspection JSON**: the `__schema` payload.
* **A GraphQL endpoint URL**: typeship runs the standard introspection query server-side and generates from the result. The endpoint becomes the client's default `baseUrl`. Endpoints with introspection disabled need the SDL instead.

## What the schema cannot say

A GraphQL schema has no equivalent of OpenAPI's `servers` and `securitySchemes`. Where the API lives and how requests authenticate come from the project's [config](https://typeship.dev/docs/projects/config#graphql-schemas), or from the endpoint and auth fields the generator shows when it sees a GraphQL input:

* **Endpoint**: every request is one `POST` to it, and it becomes the client's default `baseUrl`. A schema fetched from its own endpoint already has one. Named environments (sandbox, production) become client environments.
* **Auth**: `bearer` by default, sent as `Authorization: Bearer`. `basic` suits key-pair APIs, with the public key as the username and the private key as the password. `api_key` sends a header. `none` generates no credential option. The generated README, AGENTS.md, CLI, and MCP server all describe the scheme that was chosen.
* **Name**: the package and client names (`braintree`, `BraintreeClient`) come from `title`, or from the endpoint's host when it is not set.

Headers an API needs on every call, such as a version header, go in `defaultHeaders` on the client.

## The mapping

* Each root field becomes an operation. Queries land on `client.query`, mutations on `client.mutation`. Field arguments become a typed body object, required when any argument is non-null.
* Object types carry an optional `__typename` literal. Unions, and interfaces with implementers, become a union of their concrete types, so results narrow on `__typename`.
* Custom scalars map to `unknown`. Built-ins map to their TypeScript equivalents.
* Subscriptions are skipped, with a warning.
* Operation documents (files of queries you already wrote) are not an input. Generation works from the schema alone.

## Selections

A schema does not say which fields to fetch. Generated methods select every scalar and enum field to depth 2 by default, with a `... on` fragment for every concrete member of a union or interface. Every object-returning method takes an optional `select` argument after its body. A field that returns a scalar takes none.

In TypeScript, `select` is a typed object checked against the schema. A typo is a compile error, and the result type narrows to exactly what was picked:

```ts
const client = new BraintreeClient({ baseUrl: "https://payments.sandbox.braintree-api.com/graphql", basicAuth: { username: publicKey, password: privateKey } });

const whole = await client.query.transaction({ id: "txn_1" });               // default selection
const slim = await client.query.transaction({ id: "txn_1" }, { id: true, amount: true, customer: { email: true } });
// slim.data: { id: string; amount: number; customer: { email: string | null } | null } | null
```

Unions and interfaces take `on`, keyed by concrete type name, and come back discriminated by `__typename`:

```ts
const node = await client.query.node({ id }, { id: true, __typename: true, on: { Transaction: { amount: true }, Customer: { email: true } } });
if (node.ok && node.data?.__typename === "Transaction") node.data.amount;
```

Each method's default is exported, so adding a field is a spread: `{ ...queryTransactionSelection, status: true }`. A raw selection-set string (`"{ id amount }"`) is the escape hatch and returns the full type. `Selection<T>` and `Selected<T, S>` are exported for helpers of your own.

In Python the same override is the `select=` keyword with a raw selection set (`client.query.transaction(id="txn_1", select="{ id amount }")`). In Go it is a per-call option (`client.Query.Transaction(ctx, &braintree.QueryTransactionParams{ID: "txn_1"}, braintree.WithSelection("{ id amount }"))`). The CLI exposes it as `--select`, and MCP tools accept a `select` argument.

## Connections

Relay-style connections auto-paginate. A field taking `first` and `after` and returning `edges` plus `pageInfo` (with `hasNextPage` and `endCursor`) is detected as a list operation. Iteration yields edges, and pages advance by resending the query with `after` set to the previous page's `endCursor`:

```ts
for await (const edge of client.query.transactions({ first: 50 })) {
  edge.node; // Transaction
}
```

## Errors

GraphQL reports failures in-band, as a `200` carrying an `errors` array. The runtime turns that into `GraphQLRequestError`, carrying the full errors array, on the error side of `ApiResult`. Transport failures and undocumented statuses surface as `TransportError` and `UnexpectedApiError` exactly as they do for OpenAPI. Successful payloads are unwrapped: `result.data` is the field's value, not the `{ data: { ... } }` envelope.

## Limits

* Runtime validation does not cover GraphQL operations.
* [Spec patches](https://typeship.dev/docs/projects/spec-patches) address OpenAPI documents. GraphQL schemas generate unpatched, with a warning when patches are configured.
* The free plan's 25-operation allowance counts root fields, in schema order (queries first, then mutations). The generated README says when output was capped.

## Sitemap

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