Get started with GraphQL Yoga v3
Migration from Yoga V1
Installation
You can start by installing @graphql-yoga/node
package.
yarn add @graphql-yoga/node
Server Setup
Yoga v1 no longer uses a class for constructing the server, the createServer
function is now used.
Also, the typeDefs
and resolvers
config options must now be passed to a schema
property.
import { GraphQLServer } from 'graphql-yoga'
import { typeDefs, resolvers } from './schema'
const server = new GraphQLServer({ typeDefs, resolvers })
server.start()
Load Type Definitions from a File
In Yoga v1 it was possible to provide a file path for the typeDefs
.
import * as path from 'node:path'
import { GraphQLServer } from 'graphql-yoga'
import { resolvers } from './schema'
const server = new GraphQLServer({
typeDefs: path.join(__dirname, 'type-definitions.graphql'),
resolvers
})
server.start()
For more complex loading of type-definitions please refer to graphql-tools/load-files
.
Schema Directives (previously directiveResolvers
)
In Yoga v1 you could pass a legacy graphql-tools directiveResolver
implementation to the constructor.
import { GraphQLServer } from 'graphql-yoga'
import { resolvers } from './schema'
import { uppercaseDirectiveResolverImplementation } from './uppercase-directive-resolver-implementation'
const server = new GraphQLServer({
typeDefs: /* GraphQL */ `
type Query {
hi: String
}
`,
directiveResolvers: uppercaseDirectiveResolverImplementation
})
server.start()
Before deciding upon using schema directives, you should consider whether your custom directive could be instead implemented via a field argument (abstraction).
Context
In GraphQL Yoga v2 you can use the context
property in the same way as GraphQL Yoga v1.
The value returned from the context
factory will be merged with the initial context.
The request
property within the initial context is now a Fetch API Request. It can be used for accessing all the HTTP request parameters, such as headers or the method (POST, GET).
You can learn more about the context within the
context
documentation.
import { GraphQLServer } from 'graphql-yoga'
import { typeDefs, resolvers } from './schema'
import { db } from './db'
const server = new GraphQLServer({
typeDefs,
resolvers,
context(initialContext) {
const authHeader = initialContext.request.headers['authorization'] ?? null
return { ...initialContext, db, authHeader }
}
})
server.start()
Middlewares
GraphQL Yoga v1 included graphql-middleware
for wrapping resolver functions with common logic.
GraphQL Yoga v2 no longer includes graphql-middleware
by default as using it can result in bad performance as it wraps all field resolvers within the schema.
If you cannot migrate your graphql-middleware
code to something like graphql-tools
' mapSchema
, we recommend using the @envelop/graphql-middleware
plugin.
yarn add @envelop/graphql-middleware
import { GraphQLServer } from 'graphql-yoga'
import { typeDefs, resolvers } from './schema'
// Middleware - Permissions
const code = 'supersecret'
async function isLoggedIn(resolve, parent, args, ctx, info) {
// Include your agent code as Authorization: <token> header.
const permit = ctx.request.get('Authorization') === code
if (!permit) {
throw new Error(`Not authorised!`)
}
return resolve()
}
const permissions = {
Query: {
secured: isLoggedIn,
},
Me: isLoggedIn
}
const server = new GraphQLServer({
typeDefs,
resolvers,
middleware: [permissions]
})
server.start()
Replacing GraphQL Shield
If you are using graphql-shield
you might want to have a look and see whether the following plugins might replace it:
@envelop/generic-auth
: authentication and simple (directive-based) authorization on field level@envelop/operation-field-permissions
: granular field permission access based on schema coordinates- GraphQL AuthZ: a modern and flexible GraphQL authorization layer
Subscriptions
GraphQL Yoga v1 uses the old and deprecated subscriptions-transport-ws
protocol.
GraphQL Yoga v2 comes with built-in subscription support over SSE (Server Sent Events).
One benefit of this is that you no longer need an additional library on your frontend as the SSE protocol is just simple HTTP.
Because of the protocol change, you must migrate your GraphQL clients that execute GraphQL subscription operations to use the new protocol. Please use the code snippets for your GraphQL client as listed on the handle subscription on the client documentation.
Advantages of SSE over Websockets
- Transported over simple HTTP instead of a custom protocol
- Built-in support for re-connection and event-id Simpler protocol
- No trouble with corporate firewalls doing packet inspection
Advantages of Websockets over SSE
- Real-time, two-directional communication.
SSE gotchas
- Maximum open connections limit when not using http/2
PubSub
With GraphQL Yoga v1 used the unmaintained package graphql-subscriptions
for the PubSub
implementation.
In GraphQL Yoga v2, a newly maintained PubSub implementation is built-in.
import { PubSub } from 'graphql-yoga'
const pubSub = new PubSub()
Type-safe PubSub Usage
The old PubSub implementation was not type-safe. Now it is possible to define all the events and payloads. For a full reference please check out the Subscription PubSub documentation.
import { GraphQLServer, PubSub } from 'graphql-yoga'
const pubSub = new PubSub()
const server = new GraphQLServer({
context: { pubSub },
typeDefs: /_ GraphQL _/ `
type Query {
\_: Boolean
}
type Subscription {
randomNumber: Int!
}
type Mutation {
publishRandomNumber(randomNumber: Int!): Boolean
}
`,
resolvers: {
Subscription: {
randomNumber: {
subscribe(_, \_2, context) {
return context.asyncIterator('randomNumber')
},
resolve: (value) => value
}
},
Mutation: {
publishRandomNumber(_, args, context) {
context.pubSub.publish('randomNumber', args.randomNumber)
}
}
}
})
server.start()
Filtering Events
Instead of the withFilter
function you can now use the more modular pipe
and filter
functions exported from @graphql-yoga/node
.
You can learn more about filtering and mapping values in the subscription filter and map values documentation.
import { GraphQLServer, PubSub, withFilter } from 'graphql-yoga'
const pubSub = new PubSub()
const server = new GraphQLServer({
context: { pubSub },
typeDefs: /_ GraphQL _/ `
type Query {
\_: Boolean
}
type Subscription {
randomNumber(greaterThan: Int!): Int!
}
type Mutation {
publishRandomNumber(randomNumber: Int!): Boolean
}
`,
resolvers: {
Subscription: {
randomNumber: {
subscribe: withFilter(
(_, \_2, context) => context.asyncIterator('randomNumber'),
(payload, args) => payload > args,
),
resolve: (value) => value
}
},
Mutation: {
publishRandomNumber(_, args, context) {
context.pubSub.publish('randomNumber', args.randomNumber)
}
}
}
})
server.start()