graphql-api-engineering
/graphql-api-engineering
Pomaga pisać, testować i wydawać serwer API w GraphQL Yoga, Pothos, TypeScript na Node.js i Vitest.
- Autor
- anandartin ↗
- Repozytorium
- celestial-skills ↗
- Licencja
- Apache-2.0
- Ostatnia zmiana repozytorium
- 02.11.2026
Ocena i bezpieczeństwo
- Pliki w paczce
- 4
- Skrypty
- 0
- Komentarze HTML
- 0
- Adresy URL w SKILL.md
- 3
Brak sygnałów. Repozytorium zmienione 02.11.2026, bez odwołań do wycofanych modeli i API.
Apache-2.0, otwarta: można używać i zmieniać, z zachowaniem licencji i informacji o zmianach.
Co robi i kiedy
Pomaga pisać, testować i wydawać serwer API w GraphQL Yoga, Pothos, TypeScript na Node.js i Vitest.
Oryginalny opis (EN)
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.
Jak działa
- Stack
- Commands
- Structure
- Naming
- From design and experience skills
- Conventions
- Do
- Don't
- Quality gates
- Workflow
Podgląd SKILL.md
---
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.
Pliki w paczce (4)
- AGENTS.md 7 KB
- README.md 2 KB
- SKILL.md 13 KB
- rules/graphql-api-engineering.mdc 5 KB
Wersje
W bazie nie ma innych skilli o nazwie graphql-api-engineering.
Migawka bazy z 13.11.2026 · wersja paczki a32de2ff6c25 (skrót SHA-256 całej paczki)
Pobrania w ostatnich 30 dniach: 0 · ranking
Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.
Podobne skille
- 100fastify-api-engineering
Pisze, przegląda, testuje i wydaje REST API w Fastify i TypeScript z Vitest, zamieniając projekty w kod. - 100worker-api-engineering
Buduje i wydaje API lub usługę na Cloudflare Workers w JavaScript lub TypeScript, z testami Vitest. - 100graphql-yoga
Pomaga budować API GraphQL z GraphQL Yoga na dowolnym środowisku JavaScript: subskrypcje, przesyłanie plików, cache odpowiedzi, limity głębokości, rate limiting i migracja z Apollo Server. - 100graphql-yoga-tests
Testuje serwer GraphQL Yoga przez yoga.fetch() bez sieci, sprawdza subskrypcje i dostarczanie przyrostowe oraz konfigurację produkcyjną z wyłączoną introspekcją i persisted operations. - 100graphql
Obejmuje projektowanie schematów GraphQL, Apollo Server i Client, Federation 2.x, Relay, DataLoader, persisted queries, subskrypcje i optymalizację wydajności. - 95graphql-expert
Pomaga projektować i wdrażać API GraphQL: schematy, resolvery i optymalizację wydajności.