I wanted to be able to migrate some of our codebases to use the Relay compiler on simple GraphQL fetch requests. We can get good types 'for free', LSP tooling, and a verification across schema changes.
Belay is a lightweight GraphQL client that uses the Relay compiler for type generation without bundling relay-runtime, nor increasing your bundle size with the larger compiler artifacts.
Set up Relay compiler in your app. We use a config with projects.
import { graphql } from "@puzzmo-com/belay"; export const myQuery = graphql` query MyQuery($id: ID!) { user(id: $id) { id name } } `;The Relay compiler will generate types in __generated__/MyQuery.graphql.ts.
import { query } from "@puzzmo-com/belay"; import type { MyQuery } from "./__generated__/MyQuery.graphql"; import { myQuery } from "./myQuery"; const result = await query<MyQuery>(myQuery, { variables: { id: "123" }, url: "https://api.example.com/graphql", }); if (result.data) { console.log(result.data.user.name); }import { graphql, mutate } from "@puzzmo-com/belay"; import type { UpdateUserMutation } from "./__generated__/UpdateUserMutation.graphql"; const updateUser = graphql` mutation UpdateUserMutation($id: ID!, $name: String!) { updateUser(id: $id, name: $name) { id name } } `; const result = await mutate<UpdateUserMutation>(updateUser, { variables: { id: "123", name: "New Name" }, url: "https://api.example.com/graphql", });A tagged template literal that returns the query string. Used to mark GraphQL operations for the Relay compiler.
const query = graphql` query MyQuery { viewer { id } } `; // Returns: "query MyQuery { viewer { id } }"Execute a GraphQL query.
queryString- The GraphQL query stringoptions.variables- Query variables (typed fromT["variables"])options.url- GraphQL endpoint URLoptions.headers- Optional additional headersoptions.credentials- Optional fetch credentials modeoptions.signal- Optional AbortSignal
Returns Promise<GraphQLResponse<T["response"]>> with { data?, errors? }.
Execute a GraphQL mutation. Same API as query.
type GraphQLResponse<TData> = { data?: TData; errors?: GraphQLError[]; }; type GraphQLError = { message: string; locations?: Array<{ line: number; column: number }>; path?: Array<string | number>; extensions?: Record<string, unknown>; }; type OperationType = { variables: Record<string, unknown>; response: unknown; }; type RequestOptions<TVariables> = { variables: TVariables; url: string; headers?: HeadersInit; credentials?: RequestCredentials; signal?: AbortSignal; };Belay works with the standard Relay compiler. You need a relay.config.json at your repo root.
Some examples of how to set up your config:
{ "src": "src", "language": "typescript", "schema": "schema.graphql", "output": "src/__generated__", "eagerEsModules": true }For monorepos, use sources to map directories to project names, then define each project:
{ "root": ".", "sources": { "apps/my-app": "my-app", "apps/other-app": "other-app" }, "projects": { "my-app": { "language": "typescript", "output": "apps/my-app/src/__generated__", "schema": "api-schema.graphql", "eagerEsModules": true }, "other-app": { "language": "typescript", "output": "apps/other-app/src/__generated__", "schema": "api-schema.graphql", "eagerEsModules": true } } }language: Use"typescript"for TypeScript type generationschema: Path to your GraphQL schema fileoutput: Where to write generated*.graphql.tsfileseagerEsModules: Set totruefor ES module output (recommended)sources: Maps source directories to project names (monorepo only)
# Generate types yarn relay # Watch mode yarn relay --watchThe compiler will find all graphql tagged template literals and generate corresponding type files in the __generated__ directory.