Queries & Mutations
Define Query and Mutation fields using the @gqlkit-ts/runtime API.
Prerequisites: This guide assumes you have completed the basic setup.
Query Resolvers
Use defineQuery to define Query fields. GraphQL field names are derived from the exported variable name:
- Default: the full export name is used as-is.
- If the export name contains
$, gqlkit uses the substring after the last$. - If the export name ends with
$, gqlkit reports an error.
import { defineQuery } from "../gqlkit";
import type { NoArgs } from "@gqlkit-ts/runtime";
import type { User } from "./user";
// Query.me
export const me = defineQuery<NoArgs, User | null>(
(_root, _args, ctx) => {
return ctx.db.findUser(ctx.userId);
}
);
// Query.user(id: String!)
export const user = defineQuery<{ id: string }, User | null>(
(_root, args, ctx) => {
return ctx.db.findUser(args.id);
}
);
// Query.users
export const users = defineQuery<NoArgs, User[]>(
(_root, _args, ctx) => {
return ctx.db.findAllUsers();
}
);Generates:
type Query {
me: User
user(id: String!): User
users: [User!]!
}Example with $ delimiter:
// GraphQL field name: users
export const Query$users = defineQuery<NoArgs, User[]>(() => []);Mutation Resolvers
Use defineMutation to define Mutation fields. The same export-name rule applies:
import { defineMutation } from "../gqlkit";
import type { User, CreateUserInput } from "./user";
// Mutation.createUser(input: CreateUserInput!)
export const createUser = defineMutation<{ input: CreateUserInput }, User>(
(_root, args, ctx) => {
return ctx.db.createUser(args.input);
}
);
// Mutation.deleteUser(id: String!)
export const deleteUser = defineMutation<{ id: string }, boolean>(
(_root, args, ctx) => {
return ctx.db.deleteUser(args.id);
}
);
// GraphQL field name: createUser
export const Mutation$createUser = defineMutation<
{ input: CreateUserInput },
User
>((_root, args, ctx) => {
return ctx.db.createUser(args.input);
});Generates:
type Mutation {
createUser(input: CreateUserInput!): User!
deleteUser(id: String!): Boolean!
}Resolver Function Signature
Query and Mutation resolvers receive four arguments:
(root, args, ctx, info) => ReturnType| Argument | Description |
|---|---|
root | The root value (usually undefined) |
args | The arguments passed to the field |
ctx | The context object (typed via createGqlkitApis<Context>()) |
info | GraphQL resolve info |
NoArgs Type
Use the NoArgs type when a field has no arguments:
import { type NoArgs } from "@gqlkit-ts/runtime";
export const me = defineQuery<NoArgs, User | null>(
(_root, _args, ctx) => ctx.currentUser
);Arguments with Input Types
Use Input types for complex arguments:
export type SearchUsersInput = {
query: string;
limit: number | null;
};
export const searchUsers = defineQuery<{ input: SearchUsersInput }, User[]>(
(_root, args) => {
return findUsers(args.input.query, args.input.limit ?? 10);
}
);Inline Object Arguments
Arguments can use inline object literals. gqlkit automatically generates Input Object types:
export const searchUsers = defineQuery<
{
/** Search filter */
filter: {
namePattern: string | null;
status: UserStatus | null;
};
},
User[]
>((_root, args) => []);Generates:
type Query {
searchUsers(
"""Search filter"""
filter: SearchUsersFilterInput!
): [User!]!
}
input SearchUsersFilterInput {
namePattern: String
status: UserStatus
}Inline string literal unions and external TypeScript enums in arguments are also automatically converted to GraphQL enum types. See Inline Enums for details.
See Field Resolvers for more details on inline object arguments.
Inline Payload Types
Return types can use inline object literals. gqlkit automatically generates GraphQL Object types with the naming convention {PascalCaseResolverName}Payload:
export const updateUser = defineMutation<
{ input: UpdateUserInput },
{ user: User; updatedAt: string }
>((_root, args, ctx) => ({
user: ctx.db.updateUser(args.input),
updatedAt: new Date().toISOString(),
}));Generates:
type Mutation {
updateUser(input: UpdateUserInput!): UpdateUserPayload!
}
type UpdateUserPayload {
user: User!
updatedAt: String!
}Inline Union Payloads
Union types with inline object literals generate GraphQL Union types. Each union member must have a __typename property with a string literal type:
export const updateUser = defineMutation<
{ input: UpdateUserInput },
| { __typename: "UpdateUserSuccess"; user: User }
| { __typename: "UpdateUserError"; message: string }
>((_root, args, ctx) => {
const result = ctx.db.updateUser(args.input);
if (result.ok) {
return { __typename: "UpdateUserSuccess", user: result.user };
}
return { __typename: "UpdateUserError", message: result.error };
});Generates:
type Mutation {
updateUser(input: UpdateUserInput!): UpdateUserPayload!
}
union UpdateUserPayload = UpdateUserError | UpdateUserSuccess
type UpdateUserSuccess {
user: User!
}
type UpdateUserError {
message: String!
}The __resolveType function is automatically generated based on the __typename property. See Abstract Type Resolution for more details.
Inline Enum Payloads
String literal unions in return types generate GraphQL Enum types:
export const getStatus = defineQuery<NoArgs, "active" | "inactive" | "pending">(
(_root, _args, ctx) => ctx.db.getStatus()
);Generates:
type Query {
getStatus: GetStatusPayload!
}
enum GetStatusPayload {
ACTIVE
INACTIVE
PENDING
}Nested Inline Types
Inline types can be nested within payload objects:
export const createOrder = defineMutation<
{ input: CreateOrderInput },
{
order: {
id: string;
status: "pending" | "confirmed";
items: { productId: string; quantity: number }[];
};
}
>(/* ... */);Generates:
type CreateOrderPayload {
order: CreateOrderPayloadOrder!
}
type CreateOrderPayloadOrder {
id: String!
status: CreateOrderPayloadOrderStatus!
items: [CreateOrderPayloadOrderItems!]!
}
enum CreateOrderPayloadOrderStatus {
PENDING
CONFIRMED
}
type CreateOrderPayloadOrderItems {
productId: String!
quantity: Float!
}Attaching Directives
Add a third type parameter to attach directives to Query/Mutation fields:
import { defineQuery } from "../gqlkit";
import type { NoArgs } from "@gqlkit-ts/runtime";
import { type AuthDirective } from "./directives.js";
import type { User } from "./user.js";
export const me = defineQuery<
NoArgs,
User,
[AuthDirective<{ role: ["USER"] }>]
>((_root, _args, ctx) => ctx.currentUser);Generates:
type Query {
me: User! @auth(role: [USER])
}See Directives for more details on defining and using custom directives.