import type { GraphQLFieldExtensions, GraphQLObjectTypeConfig, GraphQLResolveInfo, GraphQLUnionTypeConfig, GraphQLArgumentConfig, GraphQLInputFieldConfig, GraphQLScalarTypeConfig, FieldDefinitionNode } from "graphql"; import { GArg, GEnumType, GEnumTypeConfig, GEnumValueConfig, GField, GInputObjectType, GInputObjectTypeConfig, GInputType, GInterfaceField, GInterfaceType, GInterfaceTypeConfig, GList, GNonNull, GNullableInputType, GNullableType, GObjectType, GOutputType, GScalarType, GType, GUnionType, InferValueFromOutputType, InferValueFromArgs, InferValueFromInputType } from "./types.js"; type SomeTypeThatIsntARecordOfArgs = string; type ImpliedResolver; }, Type extends GOutputType, Context> = InferValueFromOutputType | ((args: InferValueFromArgs, context: Context, info: GraphQLResolveInfo) => InferValueFromOutputType); type Maybe = T | null | undefined; type FieldFuncArgs; }, Type extends GOutputType, Context> = { args?: Args; type: Type; description?: Maybe; deprecationReason?: Maybe; extensions?: Maybe>>; astNode?: Maybe; }; /** @deprecated */ export type InterfaceToInterfaceFields> = Interface extends GInterfaceType ? Fields : never; type InterfaceFieldsToOutputFields; }> = { [Key in keyof Fields]: Fields[Key] extends GInterfaceField ? GField : never; }; /** @deprecated */ export type _InterfacesToOutputFields[]> = InterfacesToOutputFields; export type { _InterfacesToOutputFields as InterfacesToOutputFields }; type InterfacesToOutputFields[]> = MergeTuple<{ [Key in keyof Interfaces]: Interfaces[Key] extends GInterfaceType ? InterfaceFieldsToOutputFields : never; }, {}>; export type InterfacesToInterfaceFields[]> = MergeTuple<{ [Key in keyof Interfaces]: Interfaces[Key] extends GInterfaceType ? Fields : never; }, {}>; type MergeTuple = T extends readonly [infer U, ...infer Rest] ? MergeTuple : Merged; export type GWithContext = { /** * Creates a GraphQL object type. * * Note this is an **output** type, if you want an input object, use * `g.inputObject`. * * When calling `g.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. * * ```ts * const Person = g.object<{ name: string }>()({ * name: "Person", * fields: { * name: g.field({ type: g.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 * * ```ts * const Person = g.object<{ name: string }>()({ * name: "Person", * fields: { * name: g.field({ type: g.String }), * excitedName: g.field({ * type: g.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 * `g>` along with making `fields` a function that * returns the object. * * ```ts * type PersonSource = { name: string; friends: PersonSource[] }; * * const Person: g> = * g.object()({ * name: "Person", * fields: () => ({ * name: g.field({ type: g.String }), * friends: g.field({ type: g.list(Person) }), * }), * }); * ``` */ object: (youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction?: { youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction: true; }) => ; } & InterfaceFieldsToOutputFields>, const Interfaces extends readonly GInterfaceType[] = []>(config: { fields: Fields | (() => Fields); interfaces?: [...Interfaces]; } & Omit, "fields" | "interfaces">) => GObjectType; /** * Create a GraphQL union type. * * A union type represents an object that could be one of a list of types. * Note it is similar to an {@link GInterfaceType interface type} except that a * union doesn't imply having a common set of fields among the member types. * * ```ts * const A = g.object<{ __typename: "A" }>()({ * name: "A", * fields: { * something: g.field({ type: g.String }), * }, * }); * const B = g.object<{ __typename: "B" }>()({ * name: "B", * fields: { * differentThing: g.field({ type: g.String }), * }, * }); * const AOrB = g.union({ * name: "AOrB", * types: [A, B], * }); * ``` */ union: >(config: Flatten ? Source : never, Context>, "types"> & { types: readonly Type[] | (() => readonly Type[]); }>) => GUnionType ? Source : never, Context>; /** * Creates a GraphQL field. * * These will generally be passed directly to the `fields` object in a * `g.object` call. * * ```ts * const Something = g.object<{ thing: string }>()({ * name: "Something", * fields: { * thing: g.field({ type: g.String }), * }, * }); * ``` */ field: , Resolve, Args extends { [Key in keyof Args]: GArg; } = {}>(field: FieldFuncArgs & (Resolve extends {} ? { resolve: ((source: Source, args: InferValueFromArgs, context: Context, info: GraphQLResolveInfo) => InferValueFromOutputType) & Resolve; } : { resolve?: ((source: Source, args: InferValueFromArgs, context: Context, info: GraphQLResolveInfo) => InferValueFromOutputType) & Resolve; })) => GField, Context>; /** * A helper to declare fields while providing the source type a single time * rather than in every resolver. * * ```ts * const nodeFields = g.fields<{ id: string }>()({ * id: g.field({ type: g.ID }), * relatedIds: g.field({ * type: g.list(g.ID), * resolve(source) { * return loadRelatedIds(source.id); * }, * }), * otherRelatedIds: g.field({ * type: g.list(g.ID), * resolve(source) { * return loadOtherRelatedIds(source.id); * }, * }), * }); * * const Person = g.object<{ * id: string; * name: string; * }>()({ * name: "Person", * fields: { * ...nodeFields, * name: g.field({ type: g.String }), * }, * }); * ``` */ fields: (youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction?: { youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction: true; }) => , any, Context>> & { [Key in keyof Source]?: GField, Source[Key], Context>; }>(fields: Fields) => Fields; /** * Creates a GraphQL interface field. * * These will generally be passed directly to the `fields` object in a * {@link g.interface} call. Interfaces fields are similar to * {@link GField regular fields} except that they **don't define how the field * is resolved**. * * ```ts * const Entity = g.interface()({ * name: "Entity", * fields: { * name: g.interfaceField({ type: g.String }), * }, * }); * ``` * * Note that {@link GField regular fields} are assignable to * {@link GInterfaceField interface fields} but the opposite is not true. This * means that you can use a regular field in an * {@link GInterfaceType interface type}. */ interfaceField: ; }, Type extends GOutputType>(field: GInterfaceField) => GInterfaceField; /** * Creates a GraphQL interface type that can be implemented by other GraphQL * object and interface types. * * ```ts * const Entity = g.interface()({ * name: "Entity", * fields: { * name: g.interfaceField({ type: g.String }), * }, * }); * * type PersonSource = { __typename: "Person"; name: string }; * * const Person = g.object()({ * name: "Person", * interfaces: [Entity], * fields: { * name: g.field({ type: g.String }), * }, * }); * * type OrganisationSource = { * __typename: "Organisation"; * name: string; * }; * * const Organisation = g.object()({ * name: "Organisation", * interfaces: [Entity], * fields: { * name: g.field({ type: g.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 `g.interfaceField` was used instead of * `g.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 `g.fields` to do that. See `g.fields` for more information about * why you should use `g.fields` instead of just defining an object the fields * and spreading that. * * ```ts * const nodeFields = g.fields<{ id: string }>({ * id: g.field({ type: g.ID }), * }); * * const Node = g.field({ * name: "Node", * fields: nodeFields, * }); * * const Person = g.object<{ * __typename: "Person"; * id: string; * name: string; * }>()({ * name: "Person", * interfaces: [Node], * fields: { * ...nodeFields, * name: g.field({ type: g.String }), * }, * }); * ``` */ interface: (youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction?: { youOnlyNeedToPassATypeParameterToThisFunctionYouPassTheActualRuntimeArgsOnTheResultOfThisFunction: true; }) => , Context>; } & InterfacesToInterfaceFields, const Interfaces extends readonly GInterfaceType[] = []>(config: GInterfaceTypeConfig) => GInterfaceType; /** * A shorthand to easily create {@link GEnumValueConfig enum values} to pass to * {@link g.enum}. * * If you need to set a `description` or `deprecationReason` for an enum * variant, you should pass values directly to `g.enum` without using * `g.enumValues`. * * ```ts * const MyEnum = g.enum({ * name: "MyEnum", * values: g.enumValues(["a", "b"]), * }); * ``` * * ```ts * const values = g.enumValues(["a", "b"]); * * assertDeepEqual(values, { * a: { value: "a" }, * b: { value: "b" }, * }); * ``` */ enumValues: (values: readonly [...Values]) => { [Key in Values[number]]: GEnumValueConfig; }; /** * Creates an {@link GEnumType enum type} with a number of * {@link GEnumValueConfig enum values}. * * ```ts * const MyEnum = g.enum({ * name: "MyEnum", * values: g.enumValues(["a", "b"]), * }); * // == * graphql` * enum MyEnum { * a * b * } * `; * ``` * * ```ts * const MyEnum = g.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") * } * `;) * ``` */ enum: >(config: GEnumTypeConfig) => GEnumType; /** * Creates a {@link GArg GraphQL argument}. * * Args can can be used as arguments on output fields: * * ```ts * g.field({ * type: g.String, * args: { * something: g.arg({ type: g.String }), * }, * resolve(source, { something }) { * return something || somethingElse; * }, * }); * // == * graphql`(something: String): String`; * ``` * * Or as fields on input objects: * * ```ts * const Something = g.inputObject({ * name: "Something", * fields: { * something: g.arg({ type: g.String }), * }, * }); * // == * graphql` * input Something { * something: String * } * `; * ``` */ arg: | undefined = undefined>(arg: Flatten<{ type: Type; } & Omit> & (undefined extends DefaultValue ? { defaultValue?: DefaultValue; } : { defaultValue: DefaultValue; })) => GArg; /** * Creates an {@link GInputObjectType input object type} * * ```ts * const Something = g.inputObject({ * name: "Something", * fields: { * something: g.arg({ type: g.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. * * ```ts * import { GInputObjectType } from "@graphql-ts/schema"; * * type SomethingInputType = GInputObjectType<{ * something: g>; * }>; * const Something: SomethingInputType = g.inputObject({ * name: "Something", * fields: () => ({ * something: g.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. * * ```ts * import { GInputObjectType } from "@graphql-ts/schema"; * * const nonCircularFields = { * thing: g.arg({ type: g.String }), * }; * type SomethingInputType = GInputObjectType< * typeof nonCircularFields & { * something: g>; * } * >; * const Something: SomethingInputType = g.inputObject({ * name: "Something", * fields: () => ({ * ...nonCircularFields, * something: g.arg({ type: Something }), * }), * }); * ``` */ inputObject: : GArg; }, IsOneOf extends boolean = false>(config: GInputObjectTypeConfig) => GInputObjectType; /** * Wraps any GraphQL type in a {@link GList list type}. * * ```ts * const stringListType = g.list(g.String); * // == * graphql`[String]`; * ``` * * When used as an input type, you will recieve an array of the inner type. * * ```ts * g.field({ * type: g.String, * args: { thing: g.arg({ type: g.list(g.String) }) }, * resolve(source, { thing }) { * const theThing: undefined | null | Array = 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'`. * * ```ts * g.field({ * type: g.list(g.String), * resolve() { * return [""]; * }, * }); * ``` * * ```ts * g.field({ * type: g.list(g.String), * resolve() { * return new Set([""]); * }, * }); * ``` * * ```ts * g.field({ * type: g.list(g.String), * resolve() { * // this will not be allowed * return "some things"; * }, * }); * ``` */ list: >(of: Of) => GList; /** * Wraps a {@link GNullableType nullable type} with a * {@link GNonNull non-nullable type}. * * 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. * * ```ts * const nonNullableString = g.nonNull(g.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. * * ```ts * g.field({ * args: { * someNonNullAndRequiredArg: g.arg({ * type: g.nonNull(g.String), * }), * someNonNullButOptionalArg: g.arg({ * type: g.nonNull(g.String), * defaultValue: "some default", * }), * }, * type: g.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. * * ```ts * g.field({ * type: g.nonNull(g.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. * * ```ts * // Argument of type 'NonNullType>' * // is not assignable to parameter of type 'NullableType'. * g.nonNull(g.nonNull(g.String)); * ``` */ nonNull: >(of: Of) => GNonNull; /** * Creates a {@link GScalarType scalar type}. * * ```ts * const BigInt = g.scalar({ * name: "BigInt", * serialize(value) { * if (typeof value !== "bigint") * throw new GraphQLError( * `unexpected value provided to BigInt scalar: ${value}` * ); * return value.toString(); * }, * parseLiteral(value) { * if (value.kind !== "StringValue") * throw new GraphQLError("BigInt only accepts values as strings"); * return globalThis.BigInt(value.value); * }, * parseValue(value) { * if (typeof value === "bigint") return value; * if (typeof value !== "string") * throw new GraphQLError("BigInt only accepts values as strings"); * return globalThis.BigInt(value); * }, * }); * // for fields on output types * g.field({ type: someScalar }); * * // for args on output fields or fields on input types * g.arg({ type: someScalar }); * ``` * * 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. */ scalar: (config: GraphQLScalarTypeConfig) => GScalarType; ID: GScalarType; String: GScalarType; Float: GScalarType; Int: GScalarType; Boolean: GScalarType; }; /** * The `gWithContext` function accepts a `Context` type parameter which binds * the returned functions so they can be used to compose GraphQL types into a * GraphQL schema. * * A simple schema with only a query type looks like this: * * ```ts * import { gWithContext } from "@graphql-ts/schema"; * import { GraphQLSchema, graphql } from "graphql"; * * type Context = {}; * * const g = gWithContext(); * type g = gWithContext.infer; * * const Query = g.object()({ * name: "Query", * fields: { * hello: g.field({ * type: g.String, * resolve() { * return "Hello!"; * }, * }), * }, * }); * * const schema = new GraphQLSchema({ * query: Query, * }); * * graphql({ * source: ` * query { * hello * } * `, * schema, * }).then((result) => { * console.log(result); * }); * ``` * * You can use pass the `schema` to `ApolloServer` and other GraphQL servers. * * You can also create a more advanced schema with other object types, circular * types, args, and mutations. See {@link GWithContext} for what the other * functions on `g` do. * * ```ts * import { gWithContext } from "@graphql-ts/schema"; * import { GraphQLSchema, graphql } from "graphql"; * import { deepEqual } from "node:assert"; * * type Context = { * todos: Map; * }; * * const g = gWithContext(); * type g = gWithContext.infer; * * type TodoItem = { * id: string; * title: string; * relatedTodos: string[]; * }; * * const Todo: g> = g.object()({ * name: "Todo", * fields: () => ({ * id: g.field({ type: g.nonNull(g.ID) }), * title: g.field({ type: g.nonNull(g.String) }), * relatedTodos: g.field({ * type: g.list(Todo), * resolve(source, _args, context) { * return source.relatedTodos * .map((id) => context.todos.get(id)) * .filter((todo) => todo !== undefined); * }, * }), * }), * }); * * const Query = g.object()({ * name: "Query", * fields: { * todos: g.field({ * type: g.list(Todo), * resolve(_source, _args, context) { * return context.todos.values(); * }, * }), * }, * }); * * const Mutation = g.object()({ * name: "Mutation", * fields: { * createTodo: g.field({ * args: { * title: g.arg({ type: g.nonNull(g.String) }), * relatedTodos: g.arg({ * type: g.nonNull(g.list(g.nonNull(g.ID))), * defaultValue: [], * }), * }, * type: Todo, * resolve(_source, { title, relatedTodos }, context) { * const todo = { title, relatedTodos, id: crypto.randomUUID() }; * context.todos.set(todo.id, todo); * return todo; * }, * }), * }, * }); * * const schema = new GraphQLSchema({ * query: Query, * mutation: Mutation, * }); * * (async () => { * const contextValue: Context = { todos: new Map() }; * { * const result = await graphql({ * source: ` * query { * todos { * title * } * } * `, * schema, * contextValue, * }); * deepEqual(result, { data: { todos: [] } }); * } * * { * const result = await graphql({ * source: ` * mutation { * createTodo(title: "Try graphql-ts") { * title * } * } * `, * schema, * contextValue, * }); * deepEqual(result, { * data: { createTodo: { title: "Try graphql-ts" } }, * }); * } * { * const result = await graphql({ * source: ` * query { * todos { * title * } * } * `, * schema, * contextValue, * }); * deepEqual(result, { * data: { todos: [{ title: "Try graphql-ts" }] }, * }); * } * })(); * ``` */ export declare function gWithContext(): GWithContext; export declare namespace gWithContext { /** * The `gWithContext.infer` type is useful particularly when defining circular * types to resolve errors from TypeScript because of the circularity. * * We recommend aliasing `gWithContext.infer` to your `g` like this to make it * easier to use: * * ```ts * import { gWithContext } from "@graphql-ts/schema"; * type Context = {}; * * const g = gWithContext(); * type g = gWithContext.infer; * * type PersonSource = { name: string; friends: PersonSource[] }; * * const Person: g> = * g.object()({ * name: "Person", * fields: () => ({ * name: g.field({ type: g.String }), * friends: g.field({ type: g.list(Person) }), * }), * }); * ``` */ type infer = T extends () => (args: any) => infer R ? R : T extends (args: any) => infer R ? R : never; } type Flatten = { [Key in keyof T]: T[Key]; } & {}; //# sourceMappingURL=output.d.ts.map