meabed/gqlts

A type-safe GraphQL query builder for moving avoidable query mistakes into the editor.

TypeScriptView on GitHub
.md

gqlts builds GraphQL queries with TypeScript instead of loose strings. It is a small example of a recurring preference: move mistakes to the editor when the runtime failure would be boring and avoidable.

Why it exists#

GraphQL gives teams a strong contract, then a lot of codebases throw part of that contract away by building queries as strings. That works until a field changes, a nested selection drifts from the type shape, or a caller ships a query that only fails after the request leaves the process.

This repo came from wanting the query shape closer to the language. Not a giant client framework. Just a small builder that makes field selection explicit and lets TypeScript catch the boring mistakes before CI or production has to.

flowchart LR A[type shape] --> B[field selection] B --> C[query builder] C --> D[GraphQL string] D --> E[server contract]

The pattern#

  • Keep the query shape close to the type shape.
  • Make field selection explicit.
  • Avoid hiding GraphQL behind a giant abstraction.
  • Let generated or inferred types do the boring checking.
  • Keep the runtime client boring: send the operation, keep { data, errors, extensions } visible, and let callers decide how strict the product boundary should be.
  • Support subscriptions, uploads, batching, and request options without making every app invent a second client beside GraphQL.

Where it earns its weight#

The repo has a CLI/runtime split, which is the right shape for this problem. Generation belongs near the schema and build loop. Runtime code belongs in the app, where latency, headers, retries, and errors have product meaning.

That split is the part I still like. It keeps schema drift out of hand-written query strings without pretending the generated client owns every production concern. Authentication, caching, retry policy, and observability still belong to the app using the client.

What I still like about it#

The useful boundary is restraint. A typed query builder can become a private language very quickly. The better version stays close to GraphQL itself, keeps the output understandable, and does not prevent engineers from reading the query they are sending.

That is the engineering principle I would carry into a production API client today: generate what removes drift, type what prevents avoidable defects, and keep the abstraction thin enough that debugging still feels like software engineering rather than archaeology.

Was this useful?

React if it helped; comment if you have a concrete question, correction, or field note.

-

Discussion (0)

Practical notes, bug stories, and disagreement with receipts are welcome.

No comments yet. A useful first comment is usually a field note: what failed, what held, or what you would check before shipping this idea.
Start the discussion
Markdown is supported. Keep it concrete and useful.