GraphQL
@zeltjs/graphql is experimental. The runtime manifest shape and generated
helper APIs may change before stable release.
GraphQL support is built around a shared runtime manifest:
schemaSdl- resolver bindings
- runtime metadata such as enum, scalar, and union mappings
The executor consumes the runtime manifest. Code-first and schema-first are frontends that produce the same manifest.
Code-first:
Resolver code + args(schema)
-> generated schema.graphql
-> generated graphql-runtime.js
-> /graphql runtime
Schema-first:
schema.graphql
-> zelt graphql codegen
-> generated typed helpers
-> resolver code
-> generated graphql-runtime.js
-> /graphql runtime
API boundary
Supported experimental app-authoring APIs:
graphql()graphqlPlugin()generateGraphqlSdl()ResolverQueryMutationResolveFieldargs()gqlScalar()GqlOutput
Generated-code APIs are exported for schema-first helpers only:
readGraphqlArgs()validateGraphqlArgs()
Advanced APIs such as createGraphqlExecutor(), executeGraphqlRequest(),
GraphqlRuntimeManifest, GeneratedGraphqlRuntime, generateSdlForResolvers(),
generateGraphqlRuntimeForResolvers(), generateSchemaFirstCodegen(),
generateSchemaFirstGraphqlRuntimeForResolvers(), metadata getters, and type
conversion helpers are not the normal application authoring surface.
Future package boundaries may split these into @zeltjs/graphql/runtime,
@zeltjs/graphql/codegen, and @zeltjs/graphql/internal.
Code-first
import { createApp, http } from '@zeltjs/core';
import { args, graphql, Query, Resolver } from '@zeltjs/graphql';
import * as v from 'valibot';
const GetProductInput = v.object({
id: v.string(),
});
type Product = {
readonly id: string;
readonly name: string;
};
@Resolver()
class ProductResolver {
@Query()
product(input = args(GetProductInput)): Product {
return { id: input.id, name: 'Keyboard' };
}
}
export const app = createApp([
http({
children: [
graphql({
path: '/graphql',
resolvers: [ProductResolver],
runtimeModule: './dist/graphql-runtime.js',
}),
],
}),
]);
args(schema) defines GraphQL field arguments from a Standard Schema and
validates them at runtime.
Schema-first
type Query {
product(id: ID!): Product
}
type Product {
id: ID!
name: String!
}
zelt graphql codegen --schema src/graphql/schema.graphql --out src/generated/graphql.ts
import { Query, Resolver } from '@zeltjs/graphql';
import { Gql } from '../../generated/graphql';
@Resolver()
class ProductResolver {
@Query()
product(input = Gql.Query.product.args()): Gql.Query.product.Result {
return { id: input.id, name: 'Keyboard' };
}
}
Additional runtime validation can be layered onto generated helpers:
@Query()
product(input = Gql.Query.product.args(GetProductInput)): Gql.Query.product.Result {
return { id: input.id, name: 'Keyboard' };
}
In schema-first mode, SDL remains the source of truth. A Standard Schema passed to generated args helpers is treated as additional validation.
args<T>() is intentionally not part of the user-facing API. Schema-first types
should come from generated helpers, not handwritten generic arguments.
Build flow
GraphQL endpoints require a generated runtime module.
Code-first:
- Write resolvers.
- Configure
graphql({ runtimeModule }). - Run
zelt buildorgraphqlPlugin(). - The plugin generates
graphql-runtime.jsand a sibling.graphqlfile. - The runtime loads the generated module.
Schema-first:
- Write
schema.graphql. - Run
zelt graphql codegen --schema ... --out .... - Write resolvers using generated
Gqlhelpers. - Configure
graphql({ runtimeModule }). - Run
zelt buildorgraphqlPlugin({ mode: 'schema-first', ... }). - The plugin generates
graphql-runtime.jsand a sibling.graphqlfile. - The runtime loads the generated module.
Automatic schema-first codegen during zelt dev is not part of this release
boundary. Use zelt graphql codegen explicitly for now.
Current limitations
Code-first
- Output type support is intentionally narrow.
- Complex GraphQL interfaces are limited.
- Code-first supports custom scalar codecs and named unions experimentally.
- Field names default to method names. Explicit names are supported through decorators where available.
- Field args use Standard Schema runtime validation and require a schema adapter for SDL generation.
Schema-first
- Schema-first codegen currently supports built-in scalars, object types,
Query, andMutation. - Custom scalars, enums, unions, interfaces, and input objects are intentionally limited or deferred.
- Schema-first support for custom scalar codecs and named unions is still limited and will be expanded separately.
- Root
QueryandMutationfields must have resolver bindings. - Object fields may rely on GraphQL default field resolution.
- Generated
Gql.Query.<field>.args()helpers are the main schema-first args API. - User-facing
args<T>()is intentionally not supported.