> ## Documentation Index
> Fetch the complete documentation index at: https://www.plain.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# GraphQL SDK

> A typed SDK for Plain's GraphQL API, auto-generated from the schema.

The `@team-plain/graphql` package provides a fully typed client for Plain's GraphQL API. It is auto-generated from the [GraphQL schema](/graphql/schema), which means every query and mutation available in the API is available in the SDK.

You can use any GraphQL client to interact with Plain's API, but this SDK gives you type safety, automatic pagination, and structured error handling out of the box.

## Installation

```bash theme={null}
npm install @team-plain/graphql
```

Supports both ESM and CJS.

## Setup

```ts theme={null}
import { PlainClient } from "@team-plain/graphql";

const client = new PlainClient({ apiKey: "plainApiKey_xxx" });
```

You will need an API key — see [authentication](/graphql/authentication) for how to create one.

## Queries

Queries are available under `client.query`. Relations on returned models are lazy-loaded — accessing them triggers a separate API call automatically.

```ts theme={null}
const customer = await client.query.customer({ customerId: "c_123" });
console.log(customer.fullName);

// Relations are lazy-loaded — accessing them makes a separate API call
const company = await customer.company;
console.log(company.name);
```

## Mutations

Mutations are available under `client.mutation`. Mutation errors are returned as typed data, not thrown as exceptions. This matches Plain's API where all mutations return `*Output` types with an optional `error` field.

```ts theme={null}
const result = await client.mutation.upsertCustomer({
  input: {
    identifier: { emailAddress: "alice@example.com" },
    onCreate: {
      fullName: "Alice",
      email: { email: "alice@example.com", isVerified: false },
    },
    onUpdate: {},
  },
});

if (result.error) {
  // Typed MutationError with message, type, code, and field-level errors
  console.error(result.error.message);
  result.error.fields?.forEach((f) => {
    console.error(`  ${f.field}: ${f.message}`);
  });
} else {
  console.log(result.customer?.id);
}
```

## Pagination

```ts theme={null}
const customers = await client.query.customers({ first: 10 });

for (const customer of customers.nodes) {
  console.log(customer.fullName);
}

// Fetch the next page
const nextPage = await customers.fetchNext();
```

## Union types

GraphQL union and interface fields are exposed as discriminated unions of model classes. Each union member has a `__typename` property for type narrowing and supports the same lazy-loading as any other model.

```ts theme={null}
const thread = await client.query.thread({ threadId: "t_123" });

// Narrow with __typename
if (thread.createdBy.__typename === "UserActor") {
  console.log(thread.createdBy.userId);

  // Lazy-load a relation on the union member
  const user = await thread.createdBy.user;
  console.log(user?.fullName);
}

// Or narrow with instanceof
import { UserActorModel } from "@team-plain/graphql";

if (thread.createdBy instanceof UserActorModel) {
  const user = await thread.createdBy.user;
}
```

Models also support querying sub-connections directly:

```ts theme={null}
const thread = await client.query.thread({ threadId: "t_123" });

// Fetch timeline entries directly from the thread model
const timelineEntries = await thread.timelineEntries({ first: 25 });

for (const entry of timelineEntries.nodes) {
  console.log(entry.entry.__typename);
}
```

## Error handling

* **Queries**: network, auth (401), forbidden (403), and rate limit (429) errors throw typed exceptions (`AuthenticationError`, `ForbiddenError`, `RateLimitError`, `NetworkError`, `PlainGraphQLError`).
* **Mutations**: return the full `*Output` type. Check `result.error` for a typed `MutationError` with `message`, `type`, `code`, and `fields[]`. This is intentional — Plain's API treats mutation errors as data.

For more details on error handling patterns, see [error handling](/graphql/error-handling).

## Migrating from `@team-plain/typescript-sdk`

If you're upgrading from the old `@team-plain/typescript-sdk` package, see the [migration guide](https://github.com/team-plain/sdk/blob/main/packages/graphql/MIGRATION.md) for a full breakdown of breaking changes.

## Resources

* [GraphQL schema](https://core-api.uk.plain.com/graphql/v1/schema.graphql) — the full schema this SDK is generated from
* [API explorer](https://app.plain.com/developer/api-explorer/) — browse and test queries interactively
* [GitHub repository](https://github.com/team-plain/sdk/tree/main/packages/graphql)
