GraphQL
GraphQL API with schema generation, resolvers, and query execution
Alongside the file-based REST API, Arcway can serve a GraphQL endpoint. You define a schema, resolvers, and (optionally) DataLoaders in a graphql/ directory; Arcway discovers them, builds an executable schema with GraphQL Yoga, and serves it at /graphql — with the request session and your DataLoaders available in every resolver.
The graphql/ directory
Put your GraphQL pieces in a top-level graphql/ folder. Each is a default export:
graphql/
schema.js # default-exports an SDL string
resolvers.js # default-exports a resolver map
loaders.js # default-exports a DataLoader factory (optional)If the directory doesn't exist, GraphQL is simply off.
Schema
graphql/schema.js default-exports your schema as an SDL string:
// graphql/schema.js
export default /* GraphQL */ `
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
}
type Query {
user(id: ID!): User
}
`Resolvers
graphql/resolvers.js default-exports a resolver map — types to fields to resolver functions. Each resolver receives (parent, args, context), where context carries the request session and your loaders:
// graphql/resolvers.js
export default {
Query: {
user: (_parent, { id }, { loaders }) => loaders.userById.load(id),
},
User: {
posts: (user, _args, { loaders }) => loaders.postsByUser.load(user.id),
},
}The context.session is resolved from the request cookies the same way the rest of the framework resolves sessions, so a resolver can authorize against the signed-in user.
DataLoaders
graphql/loaders.js default-exports a factory that returns a fresh set of DataLoaders per request — batching and de-duplicating database access to avoid the N+1 problem:
// graphql/loaders.js
import DataLoader from 'dataloader'
export default ({ db }) => ({
userById: new DataLoader(async (ids) => {
const rows = await db('users').whereIn('id', ids)
const byId = new Map(rows.map((r) => [r.id, r]))
return ids.map((id) => byId.get(id) ?? null)
}),
postsByUser: new DataLoader(async (userIds) => {
const rows = await db('posts').whereIn('userId', userIds)
return userIds.map((uid) => rows.filter((r) => r.userId === uid))
}),
})The returned loaders appear on context.loaders in every resolver.
Subscriptions
GraphQL subscriptions are served over the same WebSocket transport Arcway already runs. Define Subscription resolvers in your resolver map and they're attached automatically; subscription clients connect over the WebSocket endpoint and authenticate from the session like queries and mutations.
CORS and the playground
Yoga's own CORS handling is disabled — Arcway applies CORS at the framework level, so your existing config governs the GraphQL endpoint too. The GraphiQL playground is available in development for exploring the schema.