- mercurius
- API
- Plugin options
- HTTP endpoints
- Decorators
- app.graphql(source, context, variables, operationName)
- app.graphql.extendSchema(schema), app.graphql.defineResolvers(resolvers) and app.graphql.defineLoaders(loaders)
- app.graphql.replaceSchema(schema)
- app.graphql.transformSchema(transforms)
- app.graphql.schema
- reply.graphql(source, context, variables, operationName)
- Errors
- ErrorWithProps
- Error formatter
- API
mercurius supports the following options:
-
schema
: String, String[] or schema definition. The graphql schema. The string will be parsed. -
resolvers
: Object. The graphql resolvers. -
loaders
: Object. See defineLoaders for more details. -
schemaTransforms
: Array of schema-transformation functions. Accept a schema as an argument and return a schema. -
graphiql
: boolean | string. Serve GraphiQL on/graphiql
iftrue
or'graphiql'
. Leave empty orfalse
to disable. only applies ifonlyPersisted
option is nottrue
Note: If
routes
is false, this option does not have effects. -
jit
: Integer. The minimum number of execution a query needs to be executed before being jit'ed. -
routes
: boolean. Serves the Default:true
. A graphql endpoint is exposed at/graphql
. -
path
: string. Change default graphql/graphql
route to another one. -
context
:Function
. Result of function is passed to resolvers as a custom GraphQL context. The function receives therequest
andreply
as parameters. It is only called whenroutes
options istrue
-
prefix
: String. Change the route prefix of the graphql endpoint if enabled. -
defineMutation
: Boolean. Add the empty Mutation definition if schema is not defined (Default:false
). -
errorHandler
:Function
orboolean
. Change the default error handler (Default:true
). Note: If a custom error handler is defined, it should return the standardized response format according to GraphQL spec. -
errorFormatter
:Function
. Change the default error formatter. Allows the status code of the response to be set, and a GraphQL response for the error to be defined. This can be used to format errors for batched queries, which return a successful response overall but individual errors, or to obfuscate or format internal errors. The first argument is the error object, while the second one is the context object. -
queryDepth
:Integer
. The maximum depth allowed for a single query. Note: GraphiQL IDE sends an introspection query when it starts up. This query has a depth of 7 so when thequeryDepth
value is smaller than 7 this query will fail with aBad Request
error -
validationRules
:Function
orFunction[]
. Optional additional validation rules that the queries must satisfy in addition to those defined by the GraphQL specification. When usingFunction
, arguments include additional data from graphql request and the return value must be validation rulesFunction[]
. -
subscription
: Boolean | Object. Enable subscriptions. It uses mqemitter when it is true and exposes the pubsub interface toapp.graphql.pubsub
. To use a custom emitter set the value to an object containing the emitter.subscription.emitter
: Custom emitter.subscription.pubsub
: Custom pubsub, see Subscriptions with custom PubSub for more details. Note that when passing bothemitter
andpubsub
options,emitter
will be ignored.subscription.verifyClient
:Function
A function which can be used to validate incoming connections.subscription.context
:Function
Result of function is passed to subscription resolvers as a custom GraphQL context. The function receives theconnection
andrequest
as parameters.subscription.onConnect
:Function
A function which can be used to validate theconnection_init
payload. If defined it should return a truthy value to authorize the connection. If it returns an object the subscription context will be extended with the returned object.subscription.onDisconnect
:Function
A function which is called with the subscription context of the connection after the connection gets disconnected.subscription.keepAlive
:Integer
Optional interval in ms to send theGQL_CONNECTION_KEEP_ALIVE
message.subscription.fullWsTransport
:Boolean
Enable sending every operation via WS.
-
federationMetadata
: Boolean. Enable federation metadata support so the service can be deployed behind an Apollo Gateway -
gateway
: Object. Run the GraphQL server in gateway mode.gateway.services
: Service[] An array of GraphQL services that are part of the gatewayservice.name
: A unique name for the service. Required.service.url
: The URL of the service endpoint. It can also be anArray
of URLs and in which case all the requests will be load balanced throughout the URLs. Required.service.mandatory
:Boolean
Marks service as mandatory. If any of the mandatory services are unavailable, gateway will exit with an error. (Default:false
)service.useSecureParse
:Boolean
Marks if the service response needs to be parsed securely using secure-json-parse. (Default:false
)service.rewriteHeaders
:Function
A function that gets the original headers as a parameter and returns an object containing values that should be added to the headersservice.setResponseHeaders
:Function
A function that getsreply
as a parameter and can be used to set headers for the federated response to be sent to the client.service.initHeaders
:Function
orObject
An object or a function that returns the headers sent to the service for the initial _service SDL query.service.connections
: The number of clients to create. (Default:10
)service.agent
: An optional, fully configured undici agent/pool instance to use to perform network requests. If used, you must set all connections options on the instance as the request related options from theservice
configuration will not be applied.service.bodyTimeout
: The timeout after which a request will time out, in milliseconds. (Default:30e3
- 30 seconds)service.headersTimeout
: The amount of time the parser will wait to receive the complete HTTP headers, in milliseconds. (Default:30e3
- 30 seconds)service.keepAliveMaxTimeout
: The maximum allowed keepAliveTimeout. (Default:5e3
- 5 seconds)service.maxHeaderSize
: The maximum length of request headers in bytes. (Default:16384
- 16KiB)service.keepAlive
: The amount of time pass between the keep-alive messages sent from the gateway to the service, ifundefined
, no keep-alive messages will be sent. (Default:undefined
)service.wsUrl
: The url of the websocket endpointservice.wsConnectionParams
:Function
orObject
wsConnectionParams.connectionInitPayload
:Function
orObject
An object or a function that returns theconnection_init
payload sent to the service.wsConnectionParams.reconnect
:Boolean
Enable reconnect on connection close (Default:false
)wsConnectionParams.maxReconnectAttempts
:Number
Defines the maximum reconnect attempts if reconnect is enabled (Default:Infinity
)wsConnectionParams.connectionCallback
:Function
A function called after aconnection_ack
message is received.wsConnectionParams.failedConnectionCallback
:Function
A function called after aconnection_error
message is received, the first argument contains the message payload.wsConnectionParams.failedReconnectCallback
:Function
A function called if reconnect is enabled and maxReconnectAttempts is reached.wsConnectionParams.rewriteConnectionInitPayload
:Function
A function that gets the originalconnection_init
payload along with the context as a parameter and returns an object that replaces the originalconnection_init
payload before forwarding it to the federated service
gateway.retryServicesCount
:Number
Specifies the maximum number of retries when a service fails to start on gateway initialization. (Default: 10)gateway.retryServicesInterval
:Number
The amount of time(in milliseconds) between service retry attempts in case a service fails to start on gateway initialization. (Default: 3000)
-
persistedQueries
: A hash/query map to resolve the full query text using it's unique hash. OverridespersistedQueryProvider
. -
onlyPersisted
: Boolean. Flag to control whether to allow graphql queries other than persisted. Whentrue
, it'll make the server reject any queries that are not present in thepersistedQueries
option above. It will also disable any ide available (graphiql). RequirespersistedQueries
to be set, and overridespersistedQueryProvider
. -
persistedQueryProvider
isPersistedQuery: (request: object) => boolean
: Return true if a given request matches the desired persisted query format.getHash: (request: object) => string
: Return the hash from a given request, or falsy if this request format is not supported.getQueryFromHash: async (hash: string) => string
: Return the query for a given hash.getHashForQuery?: (query: string) => string
: Return the hash for a given query string. Do not provide if you want to skip saving new queries.saveQuery?: async (hash: string, query: string) => void
: Save a query, given its hash.notFoundError?: string
: An error message to return whengetQueryFromHash
returns no result. Defaults toBad Request
.notSupportedError?: string
: An error message to return when a query matchesisPersistedQuery
, but returns no valid hash fromgetHash
. Defaults toBad Request
.
-
allowBatchedQueries
: Boolean. Flag to control whether to allow batched queries. Whentrue
, the server supports recieving an array of queries and returns an array of results. -
compilerOptions
: Object. Configurable options for the graphql-jit compiler. For more details check https://github.com/zalando-incubator/graphql-jit
query {
dogs {
name
owner {
name
pet {
name
owner {
name
pet {
name
}
}
}
}
}
}
A queryDepth
of 6
would allow this query. 5
or less would throw with the error - unnamedQuery query exceeds the query depth limit of 5
Executed the GraphQL query passed via query string parameters. The supported query string parameters are:
query
, the GraphQL query.operationName
, the operation name to execute contained in the query.variables
, a JSON object containing the variables for the query.
Executes the GraphQL query or mutation described in the body. The payload must conform to the following JSON schema:
{
type: 'object',
properties: {
query: {
type: 'string',
description: 'the GraphQL query'
},
operationName: {
type: 'string'
},
variables: {
type: ['object', 'null'],
additionalProperties: true
}
}
}
For code from example use:
curl -H "Content-Type:application/json" -XPOST -d '{"query": "query { add(x: 2, y: 2) }"}' http://localhost:3000/graphql
Executes the GraphQL query or mutation described in the body. operationName
and variables
can not be passed using this method. The
payload contains the GraphQL query.
For code from example use:
curl -H "Content-Type:application/graphql" -XPOST -d "query { add(x: 2, y: 2) }" http://localhost:3000/graphql
Serves GraphiQL if enabled by the options.
mercurius adds the following decorators.
Decorate Server with a
graphql
method.
It calls the upstream graphql()
method with the
defined schema, and it adds { app }
to the context.
const Fastify = require('fastify')
const mercurius = require('mercurius')
const app = Fastify()
const schema = `
type Query {
add(x: Int, y: Int): Int
}
`
const resolvers = {
Query: {
add: async (_, { x, y }) => x + y
}
}
app.register(mercurius, {
schema,
resolvers
})
async function run() {
// needed so that graphql is defined
await app.ready()
const query = '{ add(x: 2, y: 2) }'
const res = await app.graphql(query)
console.log(res)
// prints:
//
// {
// data: {
// add: 4
// }
// }
}
run()
app.graphql.extendSchema(schema), app.graphql.defineResolvers(resolvers) and app.graphql.defineLoaders(loaders)
It is possible to add schemas, resolvers and loaders in separate fastify plugins, like so:
const Fastify = require('fastify')
const mercurius = require('mercurius')
const app = Fastify()
const schema = `
type Human {
name: String!
}
type Dog {
name: String!
owner: Human
}
extend type Query {
dogs: [Dog]
add(x: Int, y: Int): Int
}
`
const dogs = [
{ name: 'Max' },
{ name: 'Charlie' },
{ name: 'Buddy' },
{ name: 'Max' }
]
const owners = {
Max: {
name: 'Jennifer'
},
Charlie: {
name: 'Sarah'
},
Buddy: {
name: 'Tracy'
}
}
const resolvers = {
Query: {
dogs: async (_, args, context, info) => dogs,
add: async (_, { x, y }) => x + y
}
}
const loaders = {
Dog: {
async owner(queries, { reply }) {
return queries.map(({ obj }) => owners[obj.name])
}
}
}
app.register(mercurius)
app.register(async function (app) {
app.graphql.extendSchema(schema)
app.graphql.defineResolvers(resolvers)
app.graphql.defineLoaders(loaders)
})
async function run() {
// needed so that graphql is defined
await app.ready()
const query = '{ add(x: 2, y: 2) }'
const res = await app.graphql(query)
console.log(res)
// prints:
//
// {
// data: {
// add: 4
// }
// }
}
run()
Note: app.graphql.extendSchema
is not allowed if federationMetadata
is enabled.
It is possible to replace schema and resolvers using makeSchemaExecutable
function in separate fastify plugins, like so:
const Fastify = require('fastify')
const mercurius = require('mercurius')
const { makeExecutableSchema } = require('@graphql-tools/schema')
const app = Fastify()
app.register(mercurius, {
schema: makeExecutableSchema({
typeDefs: `
type Query {
add(x: Int, y: Int): Int
}
`,
resolvers: {
Query: {
add: async (_, { x, y }) => x + y
}
}
})
})
app.register(async function (app) {
app.graphql.replaceSchema(
makeExecutableSchema({
typeDefs: `
type Query {
add(x: Int, y: Int, z: Int): Int
}
`,
resolvers: {
Query: {
add: async (_, { x, y, z }) => x + y + z
}
}
})
)
})
async function run() {
// needed so that graphql is defined
await app.ready()
const query = '{ add(x: 2, y: 2, z: 2) }'
const res = await app.graphql(query)
console.log(res)
// prints:
//
// {
// data: {
// add: 6
// }
// }
}
run()
transforms
can be an array of functions or a single function that accept the schema and returns a schema.
It is an utility function that calls replaceSchema
underneath.
app.graphql.extendSchema(typeDefs)
app.graphql.defineResolvers(resolvers)
app.graphql.transformSchema(directive()) // or [directive()]
Provides access to the built GraphQLSchema
object that mercurius
will use to execute queries. This property will reflect any updates made by extendSchema
or replaceSchema
as well.
Decorate Reply with a
graphql
method.
It calls the upstream graphql()
function with the
defined schema, and it adds { app, reply }
to the context.
const Fastify = require('fastify')
const mercurius = require('mercurius')
const app = Fastify()
const schema = `
type Query {
add(x: Int, y: Int): Int
}
`
const resolvers = {
add: async ({ x, y }) => x + y
}
app.register(mercurius, {
schema,
resolvers
})
app.get('/', async function (req, reply) {
const query = '{ add(x: 2, y: 2) }'
return reply.graphql(query)
})
async function run() {
const res = await app.inject({
method: 'GET',
url: '/'
})
console.log(JSON.parse(res.body), {
data: {
add: 4
}
})
}
run()
Mercurius help the error handling with two useful tools.
- ErrorWithProps class
- ErrorFormatter option
ErrorWithProps can be used to create Errors to be thrown inside the resolvers or plugins.
it takes 3 parameters:
- message
- extensions
- statusCode
'use strict'
throw new ErrorWithProps('message', {
...
}, 200)
Use errors extensions
to provide additional information to query errors
GraphQL services may provide an additional entry to errors with the key extensions
in the result.
'use strict'
const Fastify = require('fastify')
const mercurius = require('mercurius')
const { ErrorWithProps } = mercurius
const users = {
1: {
id: '1',
name: 'John'
},
2: {
id: '2',
name: 'Jane'
}
}
const app = Fastify()
const schema = `
type Query {
findUser(id: String!): User
}
type User {
id: ID!
name: String
}
`
const resolvers = {
Query: {
findUser: (_, { id }) => {
const user = users[id]
if (user) return users[id]
else
throw new ErrorWithProps('Invalid User ID', {
id,
code: 'USER_ID_INVALID',
timestamp: Math.round(new Date().getTime() / 1000)
})
}
}
}
app.register(mercurius, {
schema,
resolvers
})
app.listen(3000)
To control the status code for the response, the third optional parameter can be used.
throw new mercurius.ErrorWithProps('Invalid User ID', {moreErrorInfo})
// using the defaultErrorFormatter, the response statusCode will be 500 when there is a single error
throw new mercurius.ErrorWithProps('Invalid User ID', {moreErrorInfo}, 200)
// using the defaultErrorFormatter, the response statusCode will be 200 when there is a single error
const error = new mercurius.ErrorWithProps('Invalid User ID', {moreErrorInfo}, 500)
error.data = {foo: 'bar'}
throw error
// using the defaultErrorFormatter, the response status code will be always 200 because error.data is defined
Allows the status code of the response to be set, and a GraphQL response for the error to be defined. You find out how to do this here.
By default uses the defaultErrorFormatter
, but it can be overridden in the mercurius options changing the errorFormatter parameter.
Important: using the default formatter, when the error has a data property the response status code will be always 200
While using custom error formatter, you can access the status code provided by the ErrorWithProps object via
originalError
property. Please keep in mind though, that originalError
is a non-enumerable property, meaning it won't
get serialized and/or logged as a whole.
app.register(mercurius, {
schema,
resolvers,
errorFormatter: (result) => ({ statusCode: result.errors[0].originalError.statusCode, response: result })
})