Skip to content
Skills Library
Programming · Building & coding

graphql-api-engineering

/graphql-api-engineering

Engineering skill: GraphQL API server — how to build and ship a api / service with GraphQL Yoga + Pothos, TypeScript on Node.js, Vitest. Use when writing, reviewing, testing or releasing code in this stack, and when turning a Celestial design or experience skill into code.

Licence
Apache-2.0
Last repository change
02.11.2026

Assessment and security

100/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
4
Scripts
0
HTML comments
0
URLs in SKILL.md
3
Deprecation

No signals detected. Repository changed on 02.11.2026, without references to retired models or APIs.

Licence

Apache-2.0, otwarta: można używać i zmieniać, z zachowaniem licencji i informacji o zmianach.

What it does and when to use it

Engineering skill: GraphQL API server — how to build and ship a api / service with GraphQL Yoga + Pothos, TypeScript on Node.js, Vitest. Use when writing, reviewing, testing or releasing code in this stack, and when turning a Celestial design or experience skill into code.

Original author description

Engineering skill: GraphQL API server — how to build and ship a api / service with GraphQL Yoga + Pothos, TypeScript on Node.js, Vitest. Use when writing, reviewing, testing or releasing code in this stack, and when turning a Celestial design or experience skill into code.

How it works

  1. Stack
  2. Commands
  3. Structure
  4. Naming
  5. From design and experience skills
  6. Conventions
  7. Do
  8. Don't
  9. Quality gates
  10. Workflow
Original SKILL.md headings · 172 lines, 2,023 words
SKILL.md preview
---
name: graphql-api-engineering
description: "Engineering skill: GraphQL API server — how to build and ship a api / service with GraphQL Yoga + Pothos, TypeScript on Node.js, Vitest. Use when writing, reviewing, testing or releasing code in this stack, and when turning a Celestial design or experience skill into code."
type: engineering
stack: api
core: "1.0"
checked: 2026-10-26
---

# GraphQL API server

A code-first GraphQL API in TypeScript with batched data loading, field-level authorisation, cost limits and a schema that only changes in backwards-compatible steps.

For APIs that serve several clients with different data needs: web and mobile apps sharing one backend, partner integrations, product platforms. The schema is built in TypeScript, resolvers batch database access per request, and the server runs as a container on Node with a staging environment per pull request.

## Stack
- **Language:** TypeScript on Node.js TypeScript ^5.6 · Node 22 LTS+ — Schema, resolvers and database types share one type system.
- **Framework:** GraphQL Yoga + Pothos (code-first schema builder) Yoga ^6 · Pothos ^4 · graphql ^17 — Spec-compliant server with plugins for limits and masking; the schema is typed code, not a separate SDL file to keep in sync.
- **State:** Per-request context (user, loaders, request id) — Nothing user-specific outlives the request.
- **Data:** PostgreSQL via Drizzle ORM, batched with DataLoader PostgreSQL 16+ · DataLoader ^2 — Typed queries, and one query per relation per request instead of one per row.
- **Build:** TypeScript compiler (tsc) into a container image ^5.6 — Plain JavaScript output, small image, no bundler surprises on the server.
- **Hosting:** Container on a managed runtime, with a staging environment per pull request — Horizontal scaling and a working endpoint for each change.
- **Package manager:** pnpm ^10 — Fast, strict installs and reproducible images.
- **Testing:** Vitest ^3 — Resolver and service tests, plus operation tests executed against the schema.
- **Testing:** Testcontainers ^11 — Operation tests run against a real PostgreSQL, not mocks.
- **Testing:** GraphQL Inspector ^5 — Detects breaking schema changes against main.

## Commands
- dev: `pnpm dev`
- lint: `pnpm lint && pnpm typecheck && pnpm schema:check`
- test: `pnpm test`
- build: `pnpm build`
- deploy: `docker build -t graphql-api . && pnpm deploy:staging`
- install: `pnpm install --frozen-lockfile`

## Structure
- `src/server.ts` — Yoga server, plugins (depth and cost limits, error masking, persisted operations) and health checks
- `src/schema/builder.ts` — Pothos builder with context type, scalars and auth plugin
- `src/schema/<domain>/` — Object types, queries and mutations for one domain
- `src/loaders/` — DataLoader factories, created fresh per request in the context
- `src/services/` — Business logic and database access; resolvers stay thin
- `schema.graphql` — Printed schema, committed and diffed in review
- `tests/operations/` — One test file per client operation or journey, against a real database

## Naming
- Files: kebab-case.ts
- Tests: *.test.ts beside services; tests/operations/<journey-id>.test.ts
- Branches: type/short-description, e.g. feat/invoice-line-items
- Components: Types PascalCase; fields camelCase; mutations verbNoun (createInvoice) with Input and Payload types

## From design and experience skills
This stack has no UI; design skills do not apply.

When a Celestial experience skill is in use, give each journey one end-to-end test (happy path plus its error and empty states) and name analytics events `object_action` from its measures.

## Conventions
### Code
- Prefer small, pure functions and explicit types at module boundaries. _(Easier to test, review and change.)_
- Resolvers only map arguments, call a service and return; business rules live in src/services. _(Logic is testable without GraphQL and reusable from jobs and webhooks.)_

### Components
- Components read design tokens; no raw hex values, pixel sizes or font names in component code. _(One token change restyles the product.)_

### Data
- Analytics events are object_action in snake_case and come from the experience skill’s measures. _(Names stay consistent across teams and tools.)_
- Schema changes are additive; mark fields @deprecated with a reason and remove them only when usage metrics show zero for 30 days. _(Mobile clients stay installed for months and cannot all upgrade at once.)_
- Lists use cursor-based connections with a default and maximum first argument. _(Stable pagination and a hard cap on rows per request.)_

### Errors
- Every async action has loading, empty, error and success states, matching the experience skill. _(Unhandled states are where users get stuck.)_
- Expected failures are typed results in the payload (union of success and error types); unexpected errors are masked to a generic message with a request id. _(Clients can handle business errors, and stack traces never leak.)_

### Security
- Secrets never live in the repository; read them from the environment or a secret store. _(A leaked key outlives the commit that removed it.)_
- Validate every input on the server, even when the client already did. _(Clients can be bypassed.)_
- Web pages ship a Content-Security-Policy, HSTS, X-Frame-Options and nosniff. _(Cheap protection against the most common attacks.)_
- Authorise in the schema at field level (auth scopes on types and fields), not only at the top-level query. _(Nested fields can reach data through paths the top-level check never saw.)_
- Enforce max depth, max cost per operation and max page size; first-party clients use persisted operations in production. _(One crafted query must not exhaust the database.)_

### Performance
- Every field that loads a related record or list goes through a per-request DataLoader; resolvers never query inside a loop. _(Prevents N+1 queries that turn one request into hundreds.)_

### Accessibility
- Use native elements first; add ARIA only when no native element fits. _(Native controls bring keyboard and screen-reader support for free.)_

## Do
- Trace each operation by name with OpenTelemetry and record resolver timings
- Design mutations around user intent (approveInvoice), not table updates (updateInvoiceStatus)
- Review the printed schema diff before the code diff

## Don't
- Expose database columns one-to-one as the schema
- Return null for authorisation failures without a typed error the client can act on
- Share DataLoader instances across requests

## Quality gates
| Gate | Tool | Threshold | When |
|---|---|---|---|
| Lint and types | The stack’s linter and type checker | 0 errors | Every pull request |
| Unit and component tests | The stack’s test runner | All pass; every design-skill component state covered | Every pull request |
| Accessibility | Automated audit (axe on web, platform audit on mobile) | WCAG 2.2 AA · no new violations | Every pull request |
| Performance budget | Load test of persisted operations + query counter in operation tests | p95 ≤ 300ms per operation at expected peak · ≤ 1 SQL query per relation per request | Every pull request (load test nightly) |
| Dependency and secret scan | Dependency audit + secret scanner | No high or critical findings; no secrets | Every pull request and nightly |
| Preview deploy | Hosting preview environment | A working preview link on the pull request | Every pull request |
| Schema compatibility | GraphQL Inspector diff against main | 0 breaking changes; removals only for fields deprecated 30+ days with zero recorded usage | Every pull request |

The Accessibility gate checks Celestial accessibility core v1.0 (WCAG 2.2 AA); it can change tool but is never skipped. Automated: A11Y-C1, A11Y-F1, A11Y-I1, A11Y-N1, A11Y-N2, A11Y-S1, A11Y-L2, A11Y-P1 (partly). Manual pass before release: A11Y-K1, A11Y-G1, A11Y-R1, A11Y-M1, A11Y-O1, A11Y-V1, A11Y-E1, A11Y-U1, A11Y-L1 — keyboard-only run, screen reader on each journey step, 200% zoom and 320px width, reduced motion, captions on any media. Requirement IDs come from the design and experience skills this code builds. This layer owns: **A11Y-F1** A visible focus indicator on every interactive element, never hidden behind sticky bars (WCAG 2.4.7, 2.4.11); **A11Y-R1** Text works at 200% zoom, at 320px width, with increased text spacing and in either orientation (WCAG 1.4.4, 1.4.10, 1.4.12, 1.3.4); **A11Y-M1** Motion respects the reduced-motion setting; anything moving for over 5 seconds can be paused; nothing flashes more than 3 times a second (WCAG 2.2.2, 2.3.1, 2.3.3 (AAA, advised)); **A11Y-O1** Tooltips and pop-overs shown on hover or focus can be dismissed, hovered and stay until dismissed (WCAG 1.4.13); **A11Y-I1** Images, icons and charts have text alternatives (empty for decoration); text is never an image (WCAG 1.1.1, 1.4.5); **A11Y-V1** Video and audio have captions, transcripts or audio description; nothing plays sound automatically for over 3 seconds (WCAG 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.4.2); **A11Y-K1** Every action works by keyboard in a logical order, with no keyboard traps; single-key shortcuts can be turned off or remapped (WCAG 2.1.1, 2.1.2, 2.1.4, 2.4.3); **A11Y-G1** Nothing is drag-, gesture- or motion-only: each has a single tap or click alternative, and actions fire on release so they can be cancelled (WCAG 2.5.1, 2.5.2, 2.5.4, 2.5.7); **A11Y-N1** Every page has a clear title, headings, landmarks and a skip link, and can be found more than one way (WCAG 2.4.1, 2.4.2, 2.4.5, 2.4.6); **A11Y-N2** Controls have visible labels and instructions; links say where they go; the accessible name contains the visible label (WCAG 2.4.4, 2.5.3, 3.3.2); **A11Y-S1** Status, results and errors are announced to screen readers (WCAG 4.1.3); **A11Y-E2** Never ask for the same information twice in one flow; personal fields declare their purpose so they can be autofilled (WCAG 3.3.7, 1.3.5); **A11Y-U1** Focusing or changing a field never moves the person somewhere else or submits without warning (WCAG 3.2.1, 3.2.2); **A11Y-W1** Time limits warn first and can be extended (WCAG 2.2.1); **A11Y-L2** The page language, and any passage in another language, is declared in code (WCAG 3.1.1, 3.1.2); **A11Y-P1** Structure, order, names, roles and states are in the code: native elements first, ARIA only following the WAI-ARIA patterns (WCAG 1.3.1, 1.3.2, 4.1.2).

## Workflow
- Branching: Trunk-based: short-lived branches off main, squash-merged within two days.

**Review checklist**
- Correctness: does it do what the issue asks, including edge cases?
- Readability: could a new teammate change it safely?
- Architecture: is it in the right place, with the right boundaries?
- Security: inputs, secrets, permissions, dependencies
- Performance: budgets, queries, bundle size
- Tests fail when the code is broken (flip a condition and re-run)
- One approving review; two for auth scopes, cost limits or data deletion
- A client developer reviews any new or changed type

**Definition of done**
- Acceptance criteria met and demoed on the preview link
- Tests added for new behaviour and every state
- Accessibility and performance gates green
- Analytics events named from the experience skill
- Docs or changelog updated when behaviour changes
- Printed schema updated and every new field has a description
- Operation test covers success, a typed error and an unauthorised caller

**Release**
- Small pull requests (aim for under 400 changed lines)
- CI green before review
- Squash-merge to main; main is always deployable
- Every release has a written rollback path
- Merge to main deploys to production with a canary at 10% for 30 minutes
- Database migrations run before the deploy and stay compatible with the previous release

**Incidents**
- Roll back to the previous image; block a runaway operation by name at the gateway
- Write a blameless note within 48 hours: what happened, impact, fix, prevention

## Overrides of the engineering core
- Accessibility: Not applicable — the API has no UI; error messages in payloads are written to be shown to users — Nothing is rendered here; clients own accessibility.
- Journey tests: Replaced by operation tests: one per experience-skill journey's client operations, against a real database — The API's journeys are the operations clients run, not screens.
- Performance budget: Replaced by latency and query-count budgets per operation — Web vitals do not apply to an API.

## Examples
### Type with batched relation and field auth (`src/schema/invoices/invoice.ts`)

```ts
import { builder } from '../builder';
import { CustomerRef } from '../customers/customer';

export const InvoiceRef = builder.objectRef<Invoice>('Invoice');

InvoiceRef.implement({
  authScopes: { member: true },
  fields: (t) => ({
    id: t.exposeID('id'),
    number: t.exposeString('number'),
    totalCents: t.exposeInt('totalCents'),
    customer: t.field({
      type: CustomerRef,
      // One query for all invoices in this request, not one per invoice.
      resolve: (invoice, _args, ctx) => ctx.loaders.customerById.load(invoice.customerId),
    }),
    internalNotes: t.exposeString('internalNotes', {
      nullable: true,
      authScopes: { finance: true },
    }),
  }),
});
```

## Further reading
- [GraphQL learning guide](https://graphql.org/learn/) — Schema design, pagination, execution and authorisation concepts.
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/) — GraphQL-specific guidance on depth limits, batching attacks and introspection.
- [OpenTelemetry documentation](https://opentelemetry.io/docs/) — Tracing operations and resolvers across the server and database.
Files in this package (4)
  • AGENTS.md 7 KB
  • README.md 2 KB
  • SKILL.md 13 KB
  • rules/graphql-api-engineering.mdc 5 KB

Versions

The database has no other skills named graphql-api-engineering.

Database snapshot: 13.11.2026 · package version a32de2ff6c25 (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100fastify-api-engineering
    Engineering skill: Fastify REST API — how to build and ship a api / service with Fastify, TypeScript, Vitest. Use when writing, reviewing, testing or releasing code in this stack, and when turning a Celestial design or experience skill into code.
  • 100worker-api-engineering
    Engineering skill: Edge Worker API — how to build and ship a api / service with Cloudflare Workers, JavaScript (ES modules) or TypeScript, Vitest with the Workers pool. Use when writing, reviewing, testing or releasing code in this stack, and when turning a Celestial design or…
  • 100graphql-yoga
    GraphQL Yoga is a fully featured GraphQL server from The Guild that runs on any JavaScript runtime (Node.js, Bun, Deno, Cloudflare Workers, AWS Lambda). Use this skill when asked to build a GraphQL API with graphql-yoga, add subscriptions, file uploads, response caching, depth…
  • 100graphql-yoga-tests
    Tests a GraphQL Yoga server (the-guild.dev runtime) with `yoga.fetch()` for in-process, no-network request simulation of queries and mutations, `@graphql-tools/executor-http` for subscription and incremental-delivery (streaming) tests, auth-header pass-through, and…
  • 100graphql
    GraphQL covering schema design, type system, execution model, Apollo Server/Client, Federation 2.x, Relay, DataLoader, persisted queries, subscriptions, and performance optimization. Use for \"GraphQL\", \"Apollo Server\", \"Apollo Client\", \"Federation\", \"subgraph\",…
  • 95graphql-expert
    GraphQL API design and implementation. Use when building GraphQL APIs, designing schemas, implementing resolvers, or optimizing GraphQL performance.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.