@graphql-ts/schema

Search for an npm package

@graphql-ts/schema

@graphql-ts/schema is a thin wrapper around GraphQL.js providing type-safety for constructing GraphQL Schemas while avoiding type-generation, declaration merging and decorators.

more
less
import { graphql } from "@graphql-ts/schema";
import { GraphQLSchema, graphql as runGraphQL } from "graphql";
const Query = graphql.object()({
name: "Query",
fields: {
hello: graphql.field({
type: graphql.String,
resolve() {
return "Hello!";
},
}),
},
});
const schema = new GraphQLSchema({
query: Query.graphQLType,
});
runGraphQL({
source: `
query {
hello
}
`,
schema,
}).then((result) => {
console.log(result);
});
module "@graphql-ts/schema" {

The graphql export is the primary entrypoint into @graphql-ts/schema that lets you compose GraphQL types into a GraphQL Schema

more
less

A simple schema with only a query type looks like this.

import { graphql } from "@graphql-ts/schema";
import { GraphQLSchema, graphql as runGraphQL } from "graphql";
const Query = graphql.object()({
name: "Query",
fields: {
hello: graphql.field({
type: graphql.String,
resolve() {
return "Hello!";
},
}),
},
});
const schema = new GraphQLSchema({
query: Query.graphQLType,
});
runGraphQL({
source: `
query {
hello
}
`,
schema,
}).then((result) => {
console.log(result);
});

You can use pass the graphQLSchema to ApolloServer and other GraphQL servers.

You can also create a more advanced schema with other object types, args and mutations.

import { graphql } from "@graphql-ts/schema";
import { GraphQLSchema, graphql as runGraphQL } from "graphql";
import { deepEqual } from "assert";
type TodoItem = {
title: string;
};
const todos: TodoItem[] = [];
const Todo = graphql.object<TodoItem>({
name: "Todo",
fields: {
title: graphql.field({ type: graphql.String }),
},
});
const Query = graphql.object()({
name: "Query",
fields: {
todos: graphql.field({
type: graphql.list(Todo),
resolve() {
return todos;
},
}),
},
});
const Mutation = graphql.object()({
name: "Mutation",
fields: {
createTodo: graphql.field({
args: {
title: graphql.arg({ type: graphql.String }),
},
type: Todo,
resolve(source, { title }) {
const todo = { title };
todos.push(todo);
return todo;
},
}),
},
});
const schema = new GraphQLSchema({
query: Query.graphQLType,
mutation: Mutation.graphQLType,
});
(async () => {
const result = await runGraphQL({
source: `
query {
todos {
title
}
}
`,
schema,
});
deepEqual(result, { data: { todos: [] } });
const result = await runGraphQL({
source: `
mutation {
createTodo(title: "Try
@graphql-ts — /schema") {
title
}
}
`,
schema,
});
deepEqual(result, {
data: { createTodo: { title: "Try
@graphql-ts — /schema" } },
});
const result = await runGraphQL({
source: `
query {
todos {
title
}
}
`,
schema,
});
deepEqual(result, {
data: { todos: [{ title: "Try
@graphql-ts — /schema" }] },
});
})();

For information on how to construct other types like input objects, unions, interfaces and enums and more detailed information, see the documentation in the graphql export below.

When using it, you're going to want to create your own version of it bound to your specific Context type.

export * as graphql from {

Creates a GraphQL field.

more
less

These will generally be passed directly to the fields object in a graphql.object call.

const Something = graphql.object<{ thing: string }>()({
name: "Something",
fields: {
thing: graphql.field({ type: graphql.String }),
},
});
export const field: FieldFunc<Context> = ...

Creates a GraphQL object type.

more
less

Note this is an output type, if you want an input object, use graphql.inputObject.

When calling graphql.object, you must provide a type parameter that is the source of the object type. The source is what you receive as the first argument of resolvers on this type and what you must return from resolvers of fields that return this type.

const Person = graphql.object<{ name: string }>()({
name: "Person",
fields: {
name: graphql.field({ type: graphql.String }),
},
});
// ==
graphql`
type Person {
name: String
}
`;

Writing resolvers

To do anything other than just return a field from the source type, you need to provide a resolver.

Note: TypeScript will force you to provide a resolve function if the field in the source type and the GraphQL field don't match

const Person = graphql.object<{ name: string }>()({
name: "Person",
fields: {
name: graphql.field({ type: graphql.String }),
excitedName: graphql.field({
type: graphql.String,
resolve(source, args, context, info) {
return `${source.name}!`;
},
}),
},
});

Circularity

GraphQL types will often contain references to themselves and to make TypeScript allow that, you need have an explicit type annotation of graphql.ObjectType<Source> along with making fields a function that returns the object.

type PersonSource = { name: string; friends: PersonSource[] };
const Person: graphql.ObjectType<PersonSource> =
graphql.object<PersonSource>()({
name: "Person",
fields: () => ({
name: graphql.field({ type: graphql.String }),
friends: graphql.field({ type: graphql.list(Person) }),
}),
});
export const object: ObjectTypeFunc<Context> = ...

Creates a GraphQL argument.

more
less

Args can can be used as arguments on output fields:

graphql.field({
type: graphql.String,
args: {
something: graphql.arg({ type: graphql.String }),
},
resolve(source, { something }) {
return something || somethingElse;
},
});
// ==
graphql`(something: String): String`;

Or as fields on input objects:

const Something = graphql.inputObject({
name: "Something",
fields: {
something: graphql.arg({ type: graphql.String }),
},
});
// ==
graphql`
input Something {
something: String
}
`;
export function arg<Type extends InputType, DefaultValue extends InferValueFromInputType<Type> | undefined = undefined>(arg: {
type: Type;
description?: string;
deprecationReason?: string;
} & (DefaultValue extends undefined ? {
defaultValue?: DefaultValue;
} : {
defaultValue: DefaultValue;
})): Arg<Type, DefaultValue extends undefined ? false : true>
export const Boolean: ScalarType<boolean> = ...
export const Float: ScalarType<number> = ...
export const ID: ScalarType<string> = ...
export const Int: ScalarType<number> = ...
export const String: ScalarType<string> = ...

Creates an InputObjectType

more
less
const Something = graphql.inputObject({
name: "Something",
fields: {
something: graphql.arg({ type: graphql.String }),
},
});
// ==
graphql`
input Something {
something: String
}
`;

Handling circular objects

Circular input objects require explicitly specifying the fields on the object in the type because of TypeScript's limits with circularity.

type SomethingInputType = graphql.InputObjectType<{
something: graphql.Arg<SomethingInputType>;
}>;
const Something: SomethingInputType = graphql.inputObject({
name: "Something",
fields: () => ({
something: graphql.arg({ type: Something }),
}),
});

You can specify all of your non-circular fields outside of the fields object and then use typeof to get the type to avoid writing the non-circular fields as types again.

const nonCircularFields = {
thing: graphql.arg({ type: graphql.String }),
};
type SomethingInputType = graphql.InputObjectType<
typeof nonCircularFields & {
something: graphql.Arg<SomethingInputType>;
}
>;
const Something: SomethingInputType = graphql.inputObject({
name: "Something",
fields: () => ({
...nonCircularFields,
something: graphql.arg({ type: Something }),
}),
});
export function inputObject<Fields extends {
[key: string]: Arg<InputType>;
}>(config: {
name: string;
description?: string;
fields: (() => Fields) | Fields;
}): InputObjectType<Fields>

Wraps any GraphQL type in a ListType.

more
less
const stringListType = graphql.list(graphql.String);
// ==
graphql`[String]`;

When used as an input type, you will recieve an array of the inner type.

graphql.field({
type: graphql.String,
args: { thing: graphql.arg({ type: graphql.list(graphql.String) }) },
resolve(source, { thing }) {
const theThing: undefined | null | Array<string | null> = thing;
return "";
},
});

When used as an output type, you can return an iterable of the inner type that also matches typeof val === 'object' so for example, you'll probably return an Array most of the time but you could also return a Set you couldn't return a string though, even though a string is an iterable, it doesn't match typeof val === 'object'.

graphql.field({
type: graphql.list(graphql.String),
resolve() {
return [""];
},
});
graphql.field({
type: graphql.list(graphql.String),
resolve() {
return new Set([""]);
},
});
graphql.field({
type: graphql.list(graphql.String),
resolve() {
// this will not be allowed
return "some things";
},
});
export function list<Of extends Type<any>>(of: Of): ListType<Of>

Wraps a NullableType with a NonNullType.

more
less

Types in GraphQL are always nullable by default so if you want to enforce that a type must always be there, you can use the non-null type.

const nonNullableString = graphql.nonNull(graphql.String);
// ==
graphql`String!`;

When using a non-null type as an input type, your resolver will never recieve null and consumers of your GraphQL API must provide a value for it unless you provide a default value.

graphql.field({
args: {
someNonNullAndRequiredArg: graphql.arg({
type: graphql.nonNull(graphql.String),
}),
someNonNullButOptionalArg: graphql.arg({
type: graphql.nonNull(graphql.String),
defaultValue: "some default",
}),
},
type: graphql.String,
resolve(source, args) {
// both of these will always be a string
args.someNonNullAndRequiredArg;
args.someNonNullButOptionalArg;
return "";
},
});
// ==
graphql`
fieldName(
someNonNullAndRequiredArg: String!
someNonNullButOptionalArg: String! = "some default"
): String
`;

When using a non-null type as an output type, your resolver must never return null. If you do return null(which unless you do type-casting/ts-ignore/etc. @graphql-ts/schema will not let you do) graphql-js will return an error to consumers of your GraphQL API.

Non-null types should be used very carefully on output types. If you have to do a fallible operation like a network request or etc. to get the value, it probably shouldn't be non-null. If you make a field non-null and doing the fallible operation fails, consumers of your GraphQL API will be unable to see any of the other fields on the object that the non-null field was on. For example, an id on some type is a good candidate for being non-null because if you have the item, you will already have the id so getting the id will never fail but fetching a related item from a database would be fallible so even if it will never be null in the success case, you should make it nullable.

graphql.field({
type: graphql.nonNull(graphql.String),
resolve(source, args) {
return "something";
},
});
// ==
graphql`
fieldName: String!
`;

If you try to wrap another non-null type in a non-null type again, you will get a type error.

// Argument of type 'NonNullType<ScalarType<string>>'
// is not assignable to parameter of type 'TypesExcludingNonNull'.
graphql.nonNull(graphql.nonNull(graphql.String));
export function nonNull<Of extends NullableType<any>>(of: Of): NonNullType<Of>

Creates an enum type with a number of enum values.

more
less
const MyEnum = graphql.enum({
name: "MyEnum",
values: graphql.enumValues(["a", "b"]),
});
// ==
graphql`
enum MyEnum {
a
b
}
`;
const MyEnum = graphql.enum({
name: "MyEnum",
description: "My enum does things",
values: {
something: {
description: "something something",
value: "something",
},
thing: {
description: "thing thing",
deprecationReason: "something should be used instead of thing",
value: "thing",
},
},
});
// ==
graphql`
"""
My enum does things
"""
enum MyEnum {
"""
something something
"""
something
"""
thing thing
"""
thing
@deprecated — (reason: "something should be used instead of thing")
}
`;)
export function enum<Values extends Record<string, EnumValue<unknown>>>(config: {
name: string;
description?: string;
extensions?: Readonly<import("graphql").GraphQLEnumTypeExtensions>;
values: Values;
}): EnumType<Values>

A shorthand to easily create enum values to pass to graphql.enum.

more
less

If you need to set a description or deprecationReason for an enum variant, you should pass values directly to graphql.enum without using graphql.enumValues.

const MyEnum = graphql.enum({
name: "MyEnum",
values: graphql.enumValues(["a", "b"]),
});
const values = graphql.enumValues(["a", "b"]);
assertDeepEqual(values, {
a: { value: "a" },
b: { value: "b" },
});
export function enumValues<Values extends readonly string[]>(values: readonly [...Values]): Record<Values[number], EnumValue<Values[number]>>

A helper to easily share fields across object and interface types.

more
less
const nodeFields = graphql.fields<{ id: string }>()({
id: graphql.field({ type: graphql.ID }),
});
const Node = graphql.field({
name: "Node",
fields: nodeFields,
});
const Person = graphql.object<{
__typename: "Person";
id: string;
name: string;
}>()({
name: "Person",
interfaces: [Node],
fields: {
...nodeFields,
name: graphql.field({ type: graphql.String }),
},
});

Why use graphql.fields instead of just creating an object?

The definition of Field in @graphql-ts/schema has some special things, let's look at the definition of it:

type Field<
Source,
Args extends Record<string, Arg<InputType>>,
TType extends OutputType<Context>,
Key extends string,
Context
> = ...;

There's two especially notable bits in there which need to be inferred from elsewhere, the Source and Key type params.

The Source is pretty simple and it's quite simple to see why graphql.fields is useful here. You could explicitly write it with resolvers on the first arg but you'd have to do that on every field which would get very repetitive and wouldn't work for fields without resolvers.

const someFields = graphql.fields<{ name: string }>()({
name: graphql.field({ type: graphql.String }),
});

The Key type param might seem a bit more strange though. What it's saying is that the key that a field is at is part of its TypeScript type.

This is important to be able to represent the fact that a resolver is optional if the Source has a property at the Key that matches the output type.

// this is allowed
const someFields = graphql.fields<{ name: string }>()({
name: graphql.field({ type: graphql.String }),
});
const someFields = graphql.fields<{ name: string }>()({
someName: graphql.field({
// a resolver is required here since the Source is missing a `someName` property
type: graphql.String,
}),
});

Note that there is no similar function for args since they don't need special type parameters like Field does so you can create a regular object and put args in it if you want to share them.

export const fields: FieldsFunc<Context> = ...

Creates a GraphQL interface type that can be implemented by other GraphQL object and interface types.

more
less
const Entity = graphql.interface()({
name: "Entity",
fields: {
name: graphql.interfaceField({ type: graphql.String }),
},
});
type PersonSource = { __typename: "Person"; name: string };
const Person = graphql.object<PersonSource>()({
name: "Person",
interfaces: [Entity],
fields: {
name: graphql.field({ type: graphql.String }),
},
});
type OrganisationSource = {
__typename: "Organisation";
name: string;
};
const Organisation = graphql.object<OrganisationSource>()({
name: "Organisation",
interfaces: [Entity],
fields: {
name: graphql.field({ type: graphql.String }),
},
});

Resolving Types

When using GraphQL interface and union types, there needs to a way to determine which concrete object type has been returned from a resolver. With graphql-js and @graphql-ts/schema, this is done with isTypeOf on object types and resolveType on interface and union types. Note @graphql-ts/schema does not aim to strictly type the implementation of resolveType and isTypeOf. If you don't provide resolveType or isTypeOf, a __typename property on the source type will be used, if that fails, an error will be thrown at runtime.

Fields vs Interface Fields

You might have noticed that graphql.interfaceField was used instead of graphql.field for the fields on the interfaces. This is because interfaces aren't defining implementation of fields which means that fields on an interface don't need define resolvers.

Sharing field implementations

Even though interfaces don't contain field implementations, you may still want to share field implementations between interface implementations. You can use graphql.fields to do that. See graphql.fields for more information about why you should use graphql.fields instead of just defining an object the fields and spreading that.

const nodeFields = graphql.fields<{ id: string }>({
id: graphql.field({ type: graphql.ID }),
});
const Node = graphql.field({
name: "Node",
fields: nodeFields,
});
const Person = graphql.object<{
__typename: "Person";
id: string;
name: string;
}>()({
name: "Person",
interfaces: [Node],
fields: {
...nodeFields,
name: graphql.field({ type: graphql.String }),
},
});
export const interface: InterfaceTypeFunc<Context> = ...

Creates a GraphQL interface field.

more
less

These will generally be passed directly to fields object in a graphql.interface call. Interfaces fields are similar to regular fields except that they don't define how the field is resolved.

const Entity = graphql.interface()({
name: "Entity",
fields: {
name: graphql.interfaceField({ type: graphql.String }),
},
});

Note that regular fields are assignable to interface fields but the opposite is not true. This means that you can use a regular field in an interface type.

export const interfaceField: InterfaceFieldFunc<Context> = ...

Create a GraphQL union type.

more
less

A union type represents an object that could be one of a list of types. Note it is similar to an InterfaceType except that a union doesn't imply having a common set of fields among the member types.

const A = graphql.object<{ __typename: "A" }>()({
name: "A",
fields: {
something: graphql.field({ type: graphql.String }),
},
});
const B = graphql.object<{ __typename: "B" }>()({
name: "B",
fields: {
differentThing: graphql.field({ type: graphql.String }),
},
});
const AOrB = graphql.union({
name: "AOrB",
types: [A, B],
});
export const union: UnionTypeFunc<Context> = ...
export type InferValueFromArg<TArg extends Arg<InputType>> = TArg extends unknown ? InferValueFromInputType<TArg["type"]> | AddUndefined<TArg["type"], TArg["__hasDefaultValue"]> : never
Referenced by
Unexported symbols referenced here
type AddUndefined<TInputType extends InputType, HasDefaultValue extends boolean> = TInputType extends NonNullType<any> ? never : HasDefaultValue extends true ? never : undefined
Referenced by
export type InferValueFromArgs<Args extends Record<string, Arg<InputType>>> = {
readonly [Key in keyof Args]: InferValueFromArg<Args[Key]>;
}
Referenced by
export type InferValueFromInputType<Type extends InputType> = Type extends InputNonNullTypeForInference<infer Value> ? InferValueFromInputTypeWithoutAddingNull<Value> : InferValueFromInputTypeWithoutAddingNull<Type> | null
Referenced by
Unexported symbols referenced here
type InputNonNullTypeForInference<Of extends NullableInputType> = NonNullType<Of>
Referenced by
type InferValueFromInputTypeWithoutAddingNull<Type extends InputType> = Type extends ScalarType<infer Value> ? Value : Type extends EnumType<infer Values> ? Values[keyof Values]["value"] : Type extends InputListTypeForInference<infer Value> ? InferValueFromInputType<Value>[] : Type extends InputObjectType<infer Fields> ? InferValueFromArgs<Fields> : never
Referenced by
type InputListTypeForInference<Of extends InputType> = ListType<Of>
Referenced by
export type InferValueFromOutputType<Type extends OutputType<any>> = MaybePromise<Type extends OutputNonNullTypeForInference<infer Value> ? InferValueFromOutputTypeWithoutAddingNull<Value> : InferValueFromOutputTypeWithoutAddingNull<Type> | null | undefined>
Referenced by
Unexported symbols referenced here
type MaybePromise<T> = Promise<T> | T
Referenced by
type OutputNonNullTypeForInference<Of extends NullableOutputType<any>> = NonNullType<Of>
Referenced by
type InferValueFromOutputTypeWithoutAddingNull<Type extends OutputType<any>> = Type extends ScalarType<infer Value> ? Value : Type extends EnumType<infer Values> ? Values[keyof Values]["value"] : Type extends OutputListTypeForInference<infer Value> ? object & Iterable<InferValueFromOutputType<Value>> : Type extends ObjectType<infer Source, any> ? Source : Type extends UnionType<infer Source, any> ? Source : Type extends InterfaceType<infer Source, any, any> ? Source : never
Referenced by
type OutputListTypeForInference<Of extends OutputType<any>> = ListType<Of>
Referenced by
export type InputObjectType<Fields extends {
[key: string]: Arg<InputType>;
}> = {
kind: "input";
__fields: Fields;
__context: (context: unknown) => void;
graphQLType: import("graphql").GraphQLInputObjectType;
}
Referenced by

Wraps any GraphQL type in a list type.

more
less

See the documentation of graphql.list for more information.

export type ListType<Of extends Type<any>> = {
kind: "list";
of: Of;
__context: Of["__context"];
graphQLType: import("graphql").GraphQLList<Of["graphQLType"]>;
}
Referenced by

Wraps a NullableType with a non-null type.

more
less

See the documentation for graphql.nonNull for more information.

export type NonNullType<Of extends NullableType<any>> = {
kind: "non-null";
of: Of;
__context: Of["__context"];
graphQLType: import("graphql").GraphQLNonNull<Of["graphQLType"]>;
}
Referenced by

Any nullable GraphQL input type.

more
less

Note that this is not generic over a Context like NullableOutputType is because inputs types never interact with the Context.

See also:

export type NullableInputType = ScalarType<any> | InputObjectType<any> | InputListType | EnumType<any>
Referenced by
Unexported symbols referenced here

Any input list type. This type only exists because of limitations in circular types.

more
less

If you want to represent any list input type, you should do ListType<InputType>.

type InputListType = {
kind: "list";
graphQLType: import("graphql").GraphQLList<any>;
__context: any;
}
Referenced by

A GraphQL scalar type which wraps an underlying graphql-js GraphQLScalarType with a type representing the deserialized form of the scalar. These should be created used graphql.scalar.

more
less
const someScalar = graphql.scalar<string>(new GraphQLScalarType({}));
// for fields on output types
graphql.field({ type: someScalar });
// for args on output fields or fields on input types
graphql.arg({ type: someScalar });
export type ScalarType<Type> = {
kind: "scalar";
__type: Type;
__context: (context: unknown) => void;
graphQLType: import("graphql").GraphQLScalarType;
}
Referenced by

A GraphQL enum type which wraps an underlying graphql-js GraphQLEnumType. This should be created with graphql.enum.

more
less
const MyEnum = graphql.enum({
name: "MyEnum",
values: graphql.enumValues(["a", "b"]),
});
// ==
graphql`
enum MyEnum {
a
b
}
`;
export type EnumType<Values extends Record<string, EnumValue<unknown>>> = {
kind: "enum";
values: Values;
graphQLType: import("graphql").GraphQLEnumType;
__context: (context: unknown) => void;
}
Referenced by

An individual enum value in an enum type created using graphql.enum. You can use the graphql.enumValues shorthand to create enum values more easily.

more
less

Note the value property/generic here represents the deserialized form of the enum. It does not indicate the name of the enum value that is visible in the GraphQL schema. The value can be anything, not necessarily a string. Usually though, it will be a string which is equal to the key where the value is used.

export type EnumValue<Value> = {
description?: string;
deprecationReason?: string;
value: Value;
}
Referenced by

A GraphQL argument. These should be created with graphql.arg

more
less

Args can can be used as arguments on output fields:

graphql.field({
type: graphql.String,
args: {
something: graphql.arg({ type: graphql.String }),
},
resolve(source, { something }) {
return something || somethingElse;
},
});
// ==
graphql`fieldName(something: String): String`;

Or as fields on input objects:

graphql.inputObject({
name: "Something",
fields: {
something: graphql.arg({ type: graphql.String }),
},
});
// ==
graphql`
input Something {
something: String
}
`;

When the type of an arg is non-null, the value will always exist.

graphql.field({
type: graphql.String,
args: {
something: graphql.arg({ type: graphql.nonNull(graphql.String) }),
},
resolve(source, { something }) {
// `something` will always be a string
return something;
},
});
// ==
graphql`fieldName(something: String!): String`;
export type Arg<Type extends InputType, HasDefaultValue extends boolean = boolean> = {
type: Type;
description?: string;
deprecationReason?: string;
__hasDefaultValue: HasDefaultValue;
defaultValue: unknown;
}
Referenced by

Creates a ScalarType from a graphql-js GraphQLScalarType.

more
less

You should provide a type as a type parameter which is the type of the scalar value. Note, while graphql-js allows you to express scalar types like the ID type which accepts integers and strings as both input values and return values from resolvers which are transformed into strings before calling resolvers and returning the query respectively, the type you use should be string for ID since that is what it is transformed into. @graphql-ts/schema doesn't currently express the coercion of scalars, you should instead convert values to the canonical form yourself before returning from resolvers.

const JSON = graphql.scalar<JSONValue>(GraphQLJSON);
// for fields on output types
graphql.field({ type: someScalar });
// for args on output fields or fields on input types
graphql.arg({ type: someScalar });
export function scalar<Type>(scalar: import("graphql").GraphQLScalarType): ScalarType<Type>

The particular Context type for this graphql export that is provided to field resolvers.

more
less

Below, there are many types exported which are similar to the types with the same names exported from the root of the package except that they are bound to the Context type so they can be used elsewhere without needing to be bound to the context on every usage.

export type Context = unknown
Referenced by
export type NullableType = NullableType<Context>
export type Type = Type<Context>
export type NullableOutputType = NullableOutputType<Context>
export type OutputType = OutputType<Context>
Referenced by
export type Field<Source, Args extends Record<string, Arg<InputType>>, TType extends OutputType, Key extends string> = Field<Source, Args, TType, Key, Context>
export type FieldResolver<Source, Args extends Record<string, Arg<InputType>>, TType extends OutputType> = FieldResolver<Source, Args, TType, Context>
export type ObjectType<Source> = ObjectType<Source, Context>
export type UnionType<Source> = UnionType<Source, Context>
export type InterfaceType<Source, Fields extends Record<string, InterfaceField<Record<string, Arg<InputType>>, OutputType, Context>>> = InterfaceType<Source, Fields, Context>
export type InterfaceField<Args extends Record<string, Arg<InputType>>, TType extends OutputType> = InterfaceField<Args, TType, Context>
}
export function bindGraphQLSchemaAPIToContext<Context>(): GraphQLSchemaAPIWithContext<Context>

A GraphQL output field for an ObjectType which should be created using graphql.field.

export type Field<Source, Args extends Record<string, Arg<InputType>>, TType extends OutputType<Context>, Key extends string, Context> = {
args?: Args;
type: TType;
__key: (key: Key) => void;
__source: (source: Source) => void;
__context: (context: Context) => void;
resolve?: FieldResolver<Source, Args, TType, Context>;
deprecationReason?: string;
description?: string;
extensions?: Readonly<import("graphql").GraphQLFieldExtensions<Source, Context, InferValueFromArgs<Args>>>;
}
Referenced by
export type FieldFunc<Context> = <Source, Type extends OutputType<Context>, Key extends string, Args extends {
[Key in keyof Args]: Arg<InputType>;
} = {}>(field: FieldFuncArgs<Source, Args, Type, Key, Context>) => Field<Source, Args, Type, Key, Context>
Referenced by
Unexported symbols referenced here
type FieldFuncArgs<Source, Args extends {
[Key in keyof Args]: Arg<InputType>;
}, Type extends OutputType<Context>, Key extends string, Context> = {
args?: Args;
type: Type;
deprecationReason?: string;
description?: string;
extensions?: Readonly<import("graphql").GraphQLFieldExtensions<Source, unknown>>;
} & FieldFuncResolve<Source, Args, Type, Key, Context>
Referenced by
type FieldFuncResolve<Source, Args extends {
[Key in keyof Args]: Arg<InputType>;
}, Type extends OutputType<Context>, Key extends string, Context> = [Key] extends [keyof Source] ? Source[Key] extends InferValueFromOutputType<Type> | ((args: InferValueFromArgs<Args>, context: Context, info: import("graphql").GraphQLResolveInfo) => InferValueFromOutputType<Type>) ? {
resolve?: FieldResolver<Source, SomeTypeThatIsntARecordOfArgs extends Args ? {} : Args, Type, Context>;
} : {
resolve: FieldResolver<Source, SomeTypeThatIsntARecordOfArgs extends Args ? {} : Args, Type, Context>;
} : {
resolve: FieldResolver<Source, SomeTypeThatIsntARecordOfArgs extends Args ? {} : Args, Type, Context>;
}
Referenced by
type SomeTypeThatIsntARecordOfArgs = string
Referenced by
export type FieldResolver<Source, Args extends Record<string, Arg<InputType>>, TType extends OutputType<Context>, Context> = (source: Source, args: InferValueFromArgs<Args>, context: Context, info: import("graphql").GraphQLResolveInfo) => InferValueFromOutputType<TType>
Referenced by
export type FieldsFunc<Context> = <Source>(youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction?: {
youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction: true;
}) => <Fields extends {
[Key in keyof Fields]: Field<Source, any, any, Extract<Key, string>, Context>;
}>(fields: Fields) => Fields
Referenced by
export type InterfaceField<Args extends Record<string, Arg<InputType>>, Type extends OutputType<Context>, Context> = {
args?: Args;
type: Type;
deprecationReason?: string;
description?: string;
extensions?: Readonly<import("graphql").GraphQLFieldExtensions<any, any, InferValueFromArgs<Args>>>;
}
Referenced by
export type InterfaceFieldFunc<Context> = <Source, Type extends OutputType<Context>, Args extends {
[Key in keyof Args]: Arg<InputType>;
} = {}>(field: InterfaceFieldFuncArgs<Source, Args, Type, Context>) => InterfaceField<Args, Type, Context>
Referenced by
Unexported symbols referenced here
type InterfaceFieldFuncArgs<Source, Args extends {
[Key in keyof Args]: Arg<InputType>;
}, Type extends OutputType<Context>, Context> = {
args?: Args;
type: Type;
deprecationReason?: string;
description?: string;
extensions?: Readonly<import("graphql").GraphQLFieldExtensions<Source, unknown>>;
}
Referenced by
export type InterfaceToInterfaceFields<Interface extends InterfaceType<any, any, any>> = Interface extends InterfaceType<any, infer Fields, any> ? Fields : never
Referenced by
export type InterfaceType<Source, Fields extends Record<string, InterfaceField<any, OutputType<Context>, Context>>, Context> = {
kind: "interface";
__source: (source: Source) => void;
__context: (context: Context) => void;
__fields: Fields;
graphQLType: import("graphql").GraphQLInterfaceType;
}
Referenced by
export type InterfaceTypeFunc<Context> = <Source>(youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction?: {
youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction: true;
}) => <Fields extends {
[Key in keyof Fields]: InterfaceField<any, OutputType<Context>, Context>;
} & UnionToIntersection<InterfaceToInterfaceFields<Interfaces[number]>>, Interfaces extends readonly InterfaceType<Source, any, Context>[] = []>(config: {
name: string;
description?: string;
deprecationReason?: string;
interfaces?: [...Interfaces];
resolveType?: import("graphql").GraphQLTypeResolver<Source, Context>;
fields: Fields | (() => Fields);
extensions?: Readonly<import("graphql").GraphQLInterfaceTypeExtensions>;
}) => InterfaceType<Source, Fields, Context>
Referenced by
Unexported symbols referenced here
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never
Referenced by
export type InterfacesToOutputFields<Source, Context, Interfaces extends readonly InterfaceType<Source, any, Context>[]> = UnionToIntersection<InterfaceFieldsToOutputFields<Source, Context, InterfaceToInterfaceFields<Interfaces[number]>>>
Referenced by
Unexported symbols referenced here
type InterfaceFieldsToOutputFields<Source, Context, Fields extends {
[Key in keyof Fields]: InterfaceField<any, any, Context>;
}> = {
[Key in keyof Fields]: InterfaceFieldToOutputField<Source, Context, Fields[Key], Extract<Key, string>>;
}
Referenced by
type InterfaceFieldToOutputField<Source, Context, TField extends InterfaceField<any, any, Context>, Key extends string> = TField extends InterfaceField<infer Args, infer OutputType, Context> ? Field<Source, Args, OutputType, Key, Context> : never
Referenced by

Any nullable GraphQL output type for a given Context.

more
less

See also:

export type NullableOutputType<Context> = ScalarType<any> | ObjectType<any, Context> | UnionType<any, Context> | InterfaceType<any, any, Context> | EnumType<any> | OutputListType<Context>
Referenced by
Unexported symbols referenced here

Any output list type. This type only exists because of limitations in circular types.

more
less

If you want to represent any list input type, you should do ListType<OutputType<Context>>.

type OutputListType<Context> = {
kind: "list";
of: OutputType<Context>;
graphQLType: import("graphql").GraphQLList<import("graphql").GraphQLType>;
__context: (context: Context) => void;
}
Referenced by

A GraphQL object type which should be created using graphql.object.

export type ObjectType<Source, Context> = {
kind: "object";
graphQLType: import("graphql").GraphQLObjectType;
__context: (context: Context) => void;
__source: Source;
}
Referenced by

Creates a GraphQL object type.

more
less

See the docs of graphql.object for more details.

export type ObjectTypeFunc<Context> = <Source>(youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction?: {
youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction: true;
}) => <Fields extends {
[Key in keyof Fields]: Field<Source, any, any, Extract<Key, string>, Context>;
} & InterfacesToOutputFields<Source, Context, Interfaces>, Interfaces extends readonly InterfaceType<Source, any, Context>[] = []>(config: {
name: string;
fields: Fields | (() => Fields);

A description of the object type that is visible when introspected.

more
less
type Person = { name: string };
const Person = graphql.object<Person>()({
name: "Person",
description: "A person does things!",
fields: {
name: graphql.field({ type: graphql.String }),
},
});
// ==
graphql`
"""
A person does things!
"""
type Person {
name: String
}
`;
description?: string;

A number of interfaces that the object type implements. See graphql.interface for more information.

more
less
const Node = graphql.interface<{ kind: string }>()({
name: "Node",
resolveType: (source) => source.kind,
fields: {
id: graphql.interfaceField({ type: graphql.ID }),
},
});
const Person = graphql.object<{ kind: "Person"; id: string }>()({
name: "Person",
interfaces: [Node],
fields: {
id: graphql.field({ type: graphql.ID }),
},
});
interfaces?: [...Interfaces];
isTypeOf?: import("graphql").GraphQLIsTypeOfFn<unknown, Context>;
extensions?: Readonly<import("graphql").GraphQLObjectTypeExtensions<Source, Context>>;
}) => ObjectType<Source, Context>
Referenced by
export type GraphQLSchemaAPIWithContext<Context> = {

Creates a GraphQL object type.

more
less

Note this is an output type, if you want an input object, use graphql.inputObject.

When calling graphql.object, you must provide a type parameter that is the source of the object type. The source is what you receive as the first argument of resolvers on this type and what you must return from resolvers of fields that return this type.

const Person = graphql.object<{ name: string }>()({
name: "Person",
fields: {
name: graphql.field({ type: graphql.String }),
},
});
// ==
graphql`
type Person {
name: String
}
`;

Writing resolvers

To do anything other than just return a field from the source type, you need to provide a resolver.

Note: TypeScript will force you to provide a resolve function if the field in the source type and the GraphQL field don't match

const Person = graphql.object<{ name: string }>()({
name: "Person",
fields: {
name: graphql.field({ type: graphql.String }),
excitedName: graphql.field({
type: graphql.String,
resolve(source, args, context, info) {
return `${source.name}!`;
},
}),
},
});

Circularity

GraphQL types will often contain references to themselves and to make TypeScript allow that, you need have an explicit type annotation of graphql.ObjectType<Source> along with making fields a function that returns the object.

type PersonSource = { name: string; friends: PersonSource[] };
const Person: graphql.ObjectType<PersonSource> =
graphql.object<PersonSource>()({
name: "Person",
fields: () => ({
name: graphql.field({ type: graphql.String }),
friends: graphql.field({ type: graphql.list(Person) }),
}),
});
object: ObjectTypeFunc<Context>;

Create a GraphQL union type.

more
less

A union type represents an object that could be one of a list of types. Note it is similar to an InterfaceType except that a union doesn't imply having a common set of fields among the member types.

const A = graphql.object<{ __typename: "A" }>()({
name: "A",
fields: {
something: graphql.field({ type: graphql.String }),
},
});
const B = graphql.object<{ __typename: "B" }>()({
name: "B",
fields: {
differentThing: graphql.field({ type: graphql.String }),
},
});
const AOrB = graphql.union({
name: "AOrB",
types: [A, B],
});
union: UnionTypeFunc<Context>;

Creates a GraphQL field.

more
less

These will generally be passed directly to the fields object in a graphql.object call.

const Something = graphql.object<{ thing: string }>()({
name: "Something",
fields: {
thing: graphql.field({ type: graphql.String }),
},
});
field: FieldFunc<Context>;

A helper to easily share fields across object and interface types.

more
less
const nodeFields = graphql.fields<{ id: string }>()({
id: graphql.field({ type: graphql.ID }),
});
const Node = graphql.field({
name: "Node",
fields: nodeFields,
});
const Person = graphql.object<{
__typename: "Person";
id: string;
name: string;
}>()({
name: "Person",
interfaces: [Node],
fields: {
...nodeFields,
name: graphql.field({ type: graphql.String }),
},
});

Why use graphql.fields instead of just creating an object?

The definition of Field in @graphql-ts/schema has some special things, let's look at the definition of it:

type Field<
Source,
Args extends Record<string, Arg<InputType>>,
TType extends OutputType<Context>,
Key extends string,
Context
> = ...;

There's two especially notable bits in there which need to be inferred from elsewhere, the Source and Key type params.

The Source is pretty simple and it's quite simple to see why graphql.fields is useful here. You could explicitly write it with resolvers on the first arg but you'd have to do that on every field which would get very repetitive and wouldn't work for fields without resolvers.

const someFields = graphql.fields<{ name: string }>()({
name: graphql.field({ type: graphql.String }),
});

The Key type param might seem a bit more strange though. What it's saying is that the key that a field is at is part of its TypeScript type.

This is important to be able to represent the fact that a resolver is optional if the Source has a property at the Key that matches the output type.

// this is allowed
const someFields = graphql.fields<{ name: string }>()({
name: graphql.field({ type: graphql.String }),
});
const someFields = graphql.fields<{ name: string }>()({
someName: graphql.field({
// a resolver is required here since the Source is missing a `someName` property
type: graphql.String,
}),
});

Note that there is no similar function for args since they don't need special type parameters like Field does so you can create a regular object and put args in it if you want to share them.

fields: FieldsFunc<Context>;

Creates a GraphQL interface field.

more
less

These will generally be passed directly to fields object in a graphql.interface call. Interfaces fields are similar to regular fields except that they don't define how the field is resolved.

const Entity = graphql.interface()({
name: "Entity",
fields: {
name: graphql.interfaceField({ type: graphql.String }),
},
});

Note that regular fields are assignable to interface fields but the opposite is not true. This means that you can use a regular field in an interface type.

interfaceField: InterfaceFieldFunc<Context>;

Creates a GraphQL interface type that can be implemented by other GraphQL object and interface types.

more
less
const Entity = graphql.interface()({
name: "Entity",
fields: {
name: graphql.interfaceField({ type: graphql.String }),
},
});
type PersonSource = { __typename: "Person"; name: string };
const Person = graphql.object<PersonSource>()({
name: "Person",
interfaces: [Entity],
fields: {
name: graphql.field({ type: graphql.String }),
},
});
type OrganisationSource = {
__typename: "Organisation";
name: string;
};
const Organisation = graphql.object<OrganisationSource>()({
name: "Organisation",
interfaces: [Entity],
fields: {
name: graphql.field({ type: graphql.String }),
},
});

Resolving Types

When using GraphQL interface and union types, there needs to a way to determine which concrete object type has been returned from a resolver. With graphql-js and @graphql-ts/schema, this is done with isTypeOf on object types and resolveType on interface and union types. Note @graphql-ts/schema does not aim to strictly type the implementation of resolveType and isTypeOf. If you don't provide resolveType or isTypeOf, a __typename property on the source type will be used, if that fails, an error will be thrown at runtime.

Fields vs Interface Fields

You might have noticed that graphql.interfaceField was used instead of graphql.field for the fields on the interfaces. This is because interfaces aren't defining implementation of fields which means that fields on an interface don't need define resolvers.

Sharing field implementations

Even though interfaces don't contain field implementations, you may still want to share field implementations between interface implementations. You can use graphql.fields to do that. See graphql.fields for more information about why you should use graphql.fields instead of just defining an object the fields and spreading that.

const nodeFields = graphql.fields<{ id: string }>({
id: graphql.field({ type: graphql.ID }),
});
const Node = graphql.field({
name: "Node",
fields: nodeFields,
});
const Person = graphql.object<{
__typename: "Person";
id: string;
name: string;
}>()({
name: "Person",
interfaces: [Node],
fields: {
...nodeFields,
name: graphql.field({ type: graphql.String }),
},
});
interface: InterfaceTypeFunc<Context>;
}
Referenced by
export type UnionType<Source, Context> = {
kind: "union";
__source: Source;
__context: (context: Context) => void;
graphQLType: import("graphql").GraphQLUnionType;
}
Referenced by
export type UnionTypeFunc<Context> = <TObjectType extends ObjectType<any, Context>>(config: {
name: string;
description?: string;
types: TObjectType[];
resolveType?: (type: TObjectType["__source"], context: Context, info: import("graphql").GraphQLResolveInfo, abstractType: import("graphql").GraphQLUnionType) => string;
}) => UnionType<TObjectType["__source"], Context>
Referenced by

Any GraphQL type for a given Context.

more
less

Note that this includes both input and output types.

You generally won't need this because you'll likely want an input or output type but there are some uses cases for it like graphql.list.

See also:

export type Type<Context> = NullableType<Context> | NonNullType<NullableType<Context>>
Referenced by

Any nullable GraphQL type for a given Context.

more
less

You generally won't need this because you'll likely want a nullable input or output type but there are some uses cases for it like graphql.nonNull.

See also:

export type NullableType<Context> = NullableInputType | NullableOutputType<Context>
Referenced by
}
module "@graphql-ts/schema/api-with-context" {
}