mercurius
Advanced tools
Comparing version 7.1.0 to 7.2.0
@@ -5,3 +5,3 @@ # Hooks | ||
By using hooks you can interact directly with the GraphQL lifecycle of Mercurius. There are GraphQL Request hooks: | ||
By using hooks you can interact directly with the GraphQL lifecycle of Mercurius. There are GraphQL Request and Subscription hooks: | ||
@@ -16,3 +16,10 @@ - [GraphQL Request Hooks](#graphql-request-hooks) | ||
- [Add errors to the GraphQL response from a hook](#add-errors-to-the-graphql-response-from-a-hook) | ||
- [GraphQL Subscription Hooks](#graphql-subscription-hooks) | ||
- [preSubscriptionParsing](#presubscriptionparsing) | ||
- [preSubscriptionExecution](#presubscriptionexecution) | ||
- [preGatewaySubscriptionExecution](#pregatewaysubscriptionexecution) | ||
- [onSubscriptionResolution](#onsubscriptionresolution) | ||
- [onSubscriptionEnd](#onsubscriptionend) | ||
**Notice:** these hooks are only supported with `async`/`await` or returning a `Promise`. | ||
@@ -24,3 +31,3 @@ | ||
There are four different hooks that you can use in GraphQL Request *(in order of execution)*: | ||
There are five different hooks that you can use in a GraphQL Request *(in order of execution)*: | ||
@@ -50,3 +57,3 @@ When registering hooks, you must wait for Mercurius to be registered in Fastify. | ||
```js | ||
fastify.addHook('preValidation', async (schema, document, context) => { | ||
fastify.graphql.addHook('preValidation', async (schema, document, context) => { | ||
await asyncMethod() | ||
@@ -63,3 +70,3 @@ }) | ||
```js | ||
fastify.addHook('preExecution', async (schema, document, context) => { | ||
fastify.graphql.addHook('preExecution', async (schema, document, context) => { | ||
const { modifiedDocument, errors } = await asyncMethod(document) | ||
@@ -82,4 +89,7 @@ | ||
Note, this hook contains service metadata in the `service` parameter: | ||
- `name`: service name | ||
```js | ||
fastify.addHook('preGatewayExecution', async (schema, document, context) => { | ||
fastify.graphql.addHook('preGatewayExecution', async (schema, document, context, service) => { | ||
const { modifiedDocument, errors } = await asyncMethod(document) | ||
@@ -97,3 +107,3 @@ | ||
```js | ||
fastify.addHook('onResolution', async (execution, context) => { | ||
fastify.graphql.addHook('onResolution', async (execution, context) => { | ||
await asyncMethod() | ||
@@ -103,3 +113,3 @@ }) | ||
### Manage Errors from a hook | ||
### Manage Errors from a request hook | ||
If you get an error during the execution of your hook, you can just throw an error and Mercurius will automatically close the GraphQL request and send the appropriate errors to the user.` | ||
@@ -110,3 +120,3 @@ | ||
```js | ||
fastify.addHook('preParsing', async (schema, source, context) => { | ||
fastify.graphql.addHook('preParsing', async (schema, source, context) => { | ||
throw new Error('Some error') | ||
@@ -124,3 +134,3 @@ }) | ||
```js | ||
fastify.addHook('preExecution', async (schema, document, context) => { | ||
fastify.graphql.addHook('preExecution', async (schema, document, context) => { | ||
return { | ||
@@ -131,3 +141,3 @@ errors: [new Error('foo')] | ||
fastify.addHook('preExecution', async (schema, document, context) => { | ||
fastify.graphql.addHook('preExecution', async (schema, document, context) => { | ||
return { | ||
@@ -156,1 +166,78 @@ errors: [new Error('bar')] | ||
``` | ||
## GraphQL Subscription Hooks | ||
It is pretty easy to understand where each hook is executed by looking at the [lifecycle page](lifecycle.md).<br> | ||
There are five different hooks that you can use in GraphQL Subscriptions *(in order of execution)*: | ||
When registering hooks, you must make sure that subscriptions are enabled and you must wait for Mercurius to be registered in Fastify. | ||
```js | ||
await fastify.ready() | ||
``` | ||
### preSubscriptionParsing | ||
If you are using the `preSubscriptionParsing` hook, you can access the GraphQL subscription query string before it is parsed. It receives the schema and context objects as other hooks. | ||
For instance, you can register some tracing events: | ||
```js | ||
fastify.graphql.addHook('preSubscriptionParsing', async (schema, source, context) => { | ||
await registerTraceEvent() | ||
}) | ||
``` | ||
### preSubscriptionExecution | ||
By the time the `preSubscriptionExecution` hook triggers, the subscription query string has been parsed into a GraphQL Document AST. | ||
```js | ||
fastify.graphql.addHook('preSubscriptionExecution', async (schema, document, context) => { | ||
await asyncMethod() | ||
}) | ||
``` | ||
### preGatewaySubscriptionExecution | ||
This hook will only be triggered in gateway mode. When in gateway mode, each hook definition will trigger when creating a subscription with a federated service. | ||
Note, this hook contains service metadata in the `service` parameter: | ||
- `name`: service name | ||
```js | ||
fastify.graphql.addHook('preGatewaySubscriptionExecution', async (schema, document, context, service) => { | ||
await asyncMethod() | ||
}) | ||
``` | ||
### onSubscriptionResolution | ||
```js | ||
fastify.graphql.addHook('onSubscriptionResolution', async (execution, context) => { | ||
await asyncMethod() | ||
}) | ||
``` | ||
### onSubscriptionEnd | ||
This hook will be triggered when a subscription ends. | ||
```js | ||
fastify.graphql.addHook('onSubscriptionEnd', async (context) => { | ||
await asyncMethod() | ||
}) | ||
``` | ||
### Manage Errors from a subscription hook | ||
If you get an error during the execution of your subscription hook, you can just throw an error and Mercurius will send the appropriate errors to the user along the websocket.` | ||
**Notice:** there are exceptions to this with the `onSubscriptionResolution` and `onSubscriptionEnd` hooks, which will close the subscription connection if an error occurs. | ||
```js | ||
fastify.graphql.addHook('preSubscriptionParsing', async (schema, source, context) => { | ||
throw new Error('Some error') | ||
}) | ||
``` |
@@ -6,3 +6,4 @@ # mercurius | ||
- [nexus](/docs/integrations/nexus.md) - Declarative, code-first and strongly typed GraphQL schema construction for TypeScript & JavaScript | ||
- [TypeGraphQL](/docs/integrations/type-graphql.md) - Modern framework for creating GraphQL API in Node.js, using only classes and decorators | ||
- [mercurius-integration-testing](/docs/integrations/mercurius-integration-testing.md) - Utility library for writing mercurius integration tests. | ||
- [@opentelemetry](/docs/integrations/open-telemetry) - A framework for collecting traces and metrics from applications. |
@@ -57,2 +57,35 @@ # Lifecycle | ||
└─▶ onResolution Hook | ||
``` | ||
## Subscription lifecycle | ||
``` | ||
Incoming GraphQL Websocket subscription data | ||
│ | ||
└─▶ Routing | ||
│ | ||
errors ◀─┴─▶ preSubscriptionParsing Hook | ||
│ | ||
errors ◀─┴─▶ Subscription Parsing | ||
│ | ||
errors ◀─┴─▶ preSubscriptionExecution Hook | ||
│ | ||
errors ◀─┴─▶ Subscription Execution | ||
│ | ||
│ | ||
└─▶ preGatewaySubscriptionExecution Hook(s) | ||
│ | ||
errors ◀─┴─▶ Gateway Subscription Execution(s) | ||
│ | ||
wait for subscription data | ||
│ | ||
subscription closed on error ◀─┴─▶ Subscription Resolution (when subscription data is received) | ||
│ | ||
└─▶ onSubscriptionResolution Hook | ||
│ | ||
keeping processing until subscription ended | ||
│ | ||
subscription closed on error ◀─┴─▶ Subscription End (when subscription stop is received) | ||
│ | ||
└─▶ onSubscriptionEnd Hook | ||
``` |
@@ -205,4 +205,4 @@ 'use strict' | ||
gateway.graphql.addHook('preGatewayExecution', async function (schema, document, context) { | ||
console.log('preGatewayExecution called') | ||
gateway.graphql.addHook('preGatewayExecution', async function (schema, document, context, service) { | ||
console.log('preGatewayExecution called', service.name) | ||
return { | ||
@@ -209,0 +209,0 @@ document, |
119
index.d.ts
@@ -29,2 +29,3 @@ import { | ||
app: FastifyInstance; | ||
reply: FastifyReply; | ||
/** | ||
@@ -65,6 +66,13 @@ * __Caution__: Only available if `subscriptions` are enabled | ||
// --------------- | ||
// Lifecycle hooks | ||
// --------------- | ||
/** | ||
* Federated GraphQL Service metadata | ||
*/ | ||
export interface MercuriusServiceMetadata { | ||
name: string; | ||
} | ||
// ------------------------ | ||
// Request Lifecycle hooks | ||
// ------------------------ | ||
/** | ||
@@ -112,2 +120,5 @@ * `preParsing` is the first hook to be executed in the GraphQL request lifecycle. The next hook will be `preValidation`. | ||
* This hook will only be triggered in gateway mode. When in gateway mode, each hook definition will trigger multiple times in a single request just before executing remote GraphQL queries on the federated services. | ||
* | ||
* Because it is a gateway hook, this hook contains service metadata in the `service` parameter: | ||
* - `name`: service name | ||
*/ | ||
@@ -119,2 +130,3 @@ export interface preGatewayExecutionHookHandler<TContext = MercuriusContext, TError extends Error = Error> { | ||
context: TContext, | ||
service: MercuriusServiceMetadata | ||
): Promise<PreExecutionHookResponse<TError> | void>; | ||
@@ -133,2 +145,67 @@ } | ||
// ----------------------------- | ||
// Subscription Lifecycle hooks | ||
// ----------------------------- | ||
/** | ||
* `preSubscriptionParsing` is the first hook to be executed in the GraphQL subscription lifecycle. The next hook will be `preSubscriptionExecution`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
export interface preSubscriptionParsingHookHandler<TContext = MercuriusContext> { | ||
( | ||
schema: GraphQLSchema, | ||
source: string, | ||
context: TContext, | ||
): Promise<void>; | ||
} | ||
/** | ||
* `preSubscriptionExecution` is the second hook to be executed in the GraphQL subscription lifecycle. The previous hook was `preSubscriptionParsing`, the next hook will be `preGatewaySubscriptionExecution`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
export interface preSubscriptionExecutionHookHandler<TContext = MercuriusContext> { | ||
( | ||
schema: GraphQLSchema, | ||
source: DocumentNode, | ||
context: TContext, | ||
): Promise<void>; | ||
} | ||
/** | ||
* `preGatewaySubscriptionExecution` is the third hook to be executed in the GraphQL subscription lifecycle. The previous hook was `preSubscriptionExecution`, the next hook will be `onSubscriptionResolution`. | ||
* This hook will only be triggered in gateway mode when subscriptions are enabled. | ||
* | ||
* Because it is a gateway hook, this hook contains service metadata in the `service` parameter: | ||
* - `name`: service name | ||
*/ | ||
export interface preGatewaySubscriptionExecutionHookHandler<TContext = MercuriusContext> { | ||
( | ||
schema: GraphQLSchema, | ||
source: DocumentNode, | ||
context: TContext, | ||
service: MercuriusServiceMetadata | ||
): Promise<void>; | ||
} | ||
/** | ||
* `onSubscriptionResolution` is the fourth hook to be executed in the GraphQL subscription lifecycle. The previous hook was `preGatewaySubscriptionExecution`, the next hook will be `onSubscriptionEnd`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
export interface onSubscriptionResolutionHookHandler<TData extends Record<string, any> = Record<string, any>, TContext = MercuriusContext> { | ||
( | ||
execution: ExecutionResult<TData>, | ||
context: TContext, | ||
): Promise<void>; | ||
} | ||
/** | ||
* `onSubscriptionEnd` is the fifth and final hook to be executed in the GraphQL subscription lifecycle. The previous hook was `onSubscriptionResolution`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
export interface onSubscriptionEndHookHandler<TContext = MercuriusContext> { | ||
( | ||
context: TContext, | ||
): Promise<void>; | ||
} | ||
interface ServiceConfig { | ||
@@ -194,3 +271,3 @@ setSchema: (schema: string) => ServiceConfig; | ||
// Lifecycle addHooks | ||
// Request lifecycle addHooks | ||
@@ -228,2 +305,34 @@ /** | ||
addHook<TData extends Record<string, any> = Record<string, any>, TContext = MercuriusContext>(name: 'onResolution', hook: onResolutionHookHandler<TData, TContext>): void; | ||
// Subscription lifecycle addHooks | ||
/** | ||
* `preSubscriptionParsing` is the first hook to be executed in the GraphQL subscription lifecycle. The next hook will be `preSubscriptionExecution`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
addHook<TContext = MercuriusContext>(name: 'preSubscriptionParsing', hook: preSubscriptionParsingHookHandler<TContext>): void; | ||
/** | ||
* `preSubscriptionExecution` is the second hook to be executed in the GraphQL subscription lifecycle. The previous hook was `preSubscriptionParsing`, the next hook will be `preGatewaySubscriptionExecution`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
addHook<TContext = MercuriusContext>(name: 'preSubscriptionExecution', hook: preSubscriptionExecutionHookHandler<TContext>): void; | ||
/** | ||
* `preGatewaySubscriptionExecution` is the third hook to be executed in the GraphQL subscription lifecycle. The previous hook was `preSubscriptionExecution`, the next hook will be `onSubscriptionResolution`. | ||
* This hook will only be triggered in gateway mode when subscriptions are enabled. | ||
*/ | ||
addHook<TContext = MercuriusContext>(name: 'preGatewaySubscriptionExecution', hook: preGatewaySubscriptionExecutionHookHandler<TContext>): void; | ||
/** | ||
* `onSubscriptionResolution` is the fourth and final hook to be executed in the GraphQL subscription lifecycle. The previous hook was `preGatewaySubscriptionExecution`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
addHook<TData extends Record<string, any> = Record<string, any>, TContext = MercuriusContext>(name: 'onSubscriptionResolution', hook: onSubscriptionResolutionHookHandler<TData, TContext>): void; | ||
/** | ||
* `onSubscriptionEnd` is the fifth and final hook to be executed in the GraphQL subscription lifecycle. The previous hook was `onSubscriptionResolution`. | ||
* This hook will only be triggered when subscriptions are enabled. | ||
*/ | ||
addHook<TContext = MercuriusContext>(name: 'onSubscriptionEnd', hook: onSubscriptionEndHookHandler<TContext>): void; | ||
} | ||
@@ -280,3 +389,3 @@ | ||
*/ | ||
schema: GraphQLSchema | string | string[]; | ||
schema?: GraphQLSchema | string | string[]; | ||
/** | ||
@@ -283,0 +392,0 @@ * Object with resolver functions |
@@ -285,3 +285,4 @@ 'use strict' | ||
document: parse(query), | ||
context: queries[queryIndex].context | ||
context: queries[queryIndex].context, | ||
service: { name: service } | ||
})) | ||
@@ -288,0 +289,0 @@ } |
@@ -9,3 +9,3 @@ 'use strict' | ||
} = require('graphql') | ||
const { preGatewayExecutionHandler } = require('../handlers') | ||
const { preGatewayExecutionHandler, preGatewaySubscriptionExecutionHandler } = require('../handlers') | ||
@@ -444,2 +444,6 @@ const kEntityResolvers = Symbol('mercurius.entity-resolvers') | ||
if (isSubscription) { | ||
// Trigger preGatewaySubscriptionExecution hook | ||
if (context.preGatewaySubscriptionExecution !== null) { | ||
await preGatewaySubscriptionExecutionHandler({ schema, document: operation, context, service }) | ||
} | ||
const subscriptionId = service.createSubscription(query, variables, pubsub.publish.bind(pubsub), context._connectionInit) | ||
@@ -453,3 +457,3 @@ return pubsub.subscribe(`${service.name}_${subscriptionId}`) | ||
if (context.preGatewayExecution !== null) { | ||
({ modifiedQuery } = await preGatewayExecutionHandler({ schema, document: operation, context })) | ||
({ modifiedQuery } = await preGatewayExecutionHandler({ schema, document: operation, context, service })) | ||
} | ||
@@ -456,0 +460,0 @@ |
'use strict' | ||
const { hooksRunner, preExecutionHooksRunner, hookRunner, preParsingHookRunner, onResolutionHookRunner } = require('./hooks') | ||
const { hooksRunner, preExecutionHooksRunner, preGatewayExecutionHooksRunner, gatewayHookRunner, hookRunner, preParsingHookRunner, onResolutionHookRunner, onEndHookRunner } = require('./hooks') | ||
const { addErrorsToContext } = require('./errors') | ||
@@ -38,3 +38,3 @@ const { print } = require('graphql') | ||
async function preGatewayExecutionHandler (request) { | ||
const { errors, modifiedDocument } = await preExecutionHooksRunner( | ||
const { errors, modifiedDocument } = await preGatewayExecutionHooksRunner( | ||
request.context.preGatewayExecution, | ||
@@ -60,2 +60,53 @@ request | ||
module.exports = { preParsingHandler, preValidationHandler, preExecutionHandler, preGatewayExecutionHandler, onResolutionHandler } | ||
async function preSubscriptionParsingHandler (request) { | ||
await hooksRunner( | ||
request.context.preSubscriptionParsing, | ||
preParsingHookRunner, | ||
request | ||
) | ||
} | ||
async function preSubscriptionExecutionHandler (request) { | ||
await hooksRunner( | ||
request.context.preSubscriptionExecution, | ||
hookRunner, | ||
request | ||
) | ||
} | ||
async function preGatewaySubscriptionExecutionHandler (request) { | ||
await hooksRunner( | ||
request.context.preGatewaySubscriptionExecution, | ||
gatewayHookRunner, | ||
request | ||
) | ||
} | ||
async function onSubscriptionResolutionHandler (request) { | ||
await hooksRunner( | ||
request.context.onSubscriptionResolution, | ||
onResolutionHookRunner, | ||
request | ||
) | ||
} | ||
async function onSubscriptionEndHandler (request) { | ||
await hooksRunner( | ||
request.context.onSubscriptionEnd, | ||
onEndHookRunner, | ||
request | ||
) | ||
} | ||
module.exports = { | ||
preParsingHandler, | ||
preValidationHandler, | ||
preExecutionHandler, | ||
preGatewayExecutionHandler, | ||
onResolutionHandler, | ||
preSubscriptionParsingHandler, | ||
preSubscriptionExecutionHandler, | ||
preGatewaySubscriptionExecutionHandler, | ||
onSubscriptionResolutionHandler, | ||
onSubscriptionEndHandler | ||
} |
'use strict' | ||
const lifecycleHooks = ['preParsing', 'preValidation', 'preExecution', 'preGatewayExecution', 'onResolution'] | ||
const lifecycleHooks = [ | ||
'preParsing', | ||
'preValidation', | ||
'preExecution', | ||
'preGatewayExecution', | ||
'onResolution', | ||
'preSubscriptionParsing', | ||
'preSubscriptionExecution', | ||
'preGatewaySubscriptionExecution', | ||
'onSubscriptionResolution', | ||
'onSubscriptionEnd' | ||
] | ||
const { MER_ERR_HOOK_INVALID_TYPE, MER_ERR_HOOK_INVALID_HANDLER, MER_ERR_HOOK_UNSUPPORTED_HOOK } = require('./errors') | ||
@@ -12,2 +23,7 @@ | ||
this.onResolution = [] | ||
this.preSubscriptionParsing = [] | ||
this.preSubscriptionExecution = [] | ||
this.preGatewaySubscriptionExecution = [] | ||
this.onSubscriptionResolution = [] | ||
this.onSubscriptionEnd = [] | ||
} | ||
@@ -34,3 +50,8 @@ | ||
preGatewayExecution: null, | ||
onResolution: null | ||
onResolution: null, | ||
preSubscriptionParsing: null, | ||
preSubscriptionExecution: null, | ||
preGatewaySubscriptionExecution: null, | ||
onSubscriptionResolution: null, | ||
onSubscriptionEnd: null | ||
} | ||
@@ -42,2 +63,7 @@ if (hooks.preParsing.length > 0) contextHooks.preParsing = hooks.preParsing.slice() | ||
if (hooks.onResolution.length > 0) contextHooks.onResolution = hooks.onResolution.slice() | ||
if (hooks.preSubscriptionParsing.length > 0) contextHooks.preSubscriptionParsing = hooks.preSubscriptionParsing.slice() | ||
if (hooks.preSubscriptionExecution.length > 0) contextHooks.preSubscriptionExecution = hooks.preSubscriptionExecution.slice() | ||
if (hooks.preGatewaySubscriptionExecution.length > 0) contextHooks.preGatewaySubscriptionExecution = hooks.preGatewaySubscriptionExecution.slice() | ||
if (hooks.onSubscriptionResolution.length > 0) contextHooks.onSubscriptionResolution = hooks.onSubscriptionResolution.slice() | ||
if (hooks.onSubscriptionEnd.length > 0) contextHooks.onSubscriptionEnd = hooks.onSubscriptionEnd.slice() | ||
return Object.assign(contextHooks, context) | ||
@@ -72,2 +98,22 @@ } | ||
async function preGatewayExecutionHooksRunner (functions, request) { | ||
let errors = [] | ||
let modifiedDocument | ||
for (const fn of functions) { | ||
const result = await fn(request.schema, modifiedDocument || request.document, request.context, request.service) | ||
if (result) { | ||
if (typeof result.document !== 'undefined') { | ||
modifiedDocument = result.document | ||
} | ||
if (typeof result.errors !== 'undefined') { | ||
errors = errors.concat(result.errors) | ||
} | ||
} | ||
} | ||
return { errors, modifiedDocument } | ||
} | ||
function hookRunner (fn, request) { | ||
@@ -77,2 +123,6 @@ return fn(request.schema, request.document, request.context) | ||
function gatewayHookRunner (fn, request) { | ||
return fn(request.schema, request.document, request.context, request.service) | ||
} | ||
function preParsingHookRunner (fn, request) { | ||
@@ -86,2 +136,6 @@ return fn(request.schema, request.source, request.context) | ||
function onEndHookRunner (fn, request) { | ||
return fn(request.context) | ||
} | ||
module.exports = { | ||
@@ -93,5 +147,8 @@ Hooks, | ||
hookRunner, | ||
gatewayHookRunner, | ||
preGatewayExecutionHooksRunner, | ||
preParsingHookRunner, | ||
onResolutionHookRunner, | ||
onEndHookRunner, | ||
lifecycleHooks | ||
} |
@@ -20,2 +20,3 @@ 'use strict' | ||
const { MER_ERR_GQL_SUBSCRIPTION_FORBIDDEN, MER_ERR_GQL_SUBSCRIPTION_UNKNOWN_EXTENSION } = require('./errors') | ||
const { preSubscriptionParsingHandler, onSubscriptionResolutionHandler, preSubscriptionExecutionHandler, onSubscriptionEndHandler } = require('./handlers') | ||
@@ -91,3 +92,3 @@ module.exports = class SubscriptionConnection { | ||
case GQL_STOP: | ||
this.handleGQLStop(data) | ||
await this.handleGQLStop(data) | ||
break | ||
@@ -172,6 +173,15 @@ default: | ||
const schema = this.fastify ? this.fastify.graphql.schema : undefined | ||
// Trigger preSubscriptionParsing hook | ||
if (this.context.preSubscriptionParsing !== null && typeof schema !== 'undefined' && typeof query === 'string') { | ||
await preSubscriptionParsingHandler({ schema, source: query, context }) | ||
} | ||
const document = typeof query !== 'string' ? query : parse(query) | ||
const schema = this.fastify ? this.fastify.graphql.schema : undefined | ||
this.subscriptionContexts.set(id, sc) | ||
// Trigger preSubscriptionExecution hook | ||
if (this.context.preSubscriptionExecution !== null && typeof schema !== 'undefined') { | ||
await preSubscriptionExecutionHandler({ schema, document, context }) | ||
} | ||
const subIter = await subscribe( | ||
@@ -202,2 +212,11 @@ schema, | ||
for await (const value of subIter) { | ||
// Trigger onSubscriptionResolution hook | ||
if (this.context.onSubscriptionResolution !== null) { | ||
try { | ||
await onSubscriptionResolutionHandler({ execution: value, context }) | ||
} catch (error) { | ||
this.fastify.log.error(error) | ||
return this.handleConnectionClose() | ||
} | ||
} | ||
this.sendMessage(GQL_DATA, data.id, value) | ||
@@ -209,3 +228,11 @@ } | ||
handleGQLStop (data) { | ||
async handleGQLStop (data) { | ||
if (this.context.onSubscriptionEnd) { | ||
try { | ||
await onSubscriptionEndHandler({ context: this.context }) | ||
} catch (error) { | ||
this.fastify.log.error(error) | ||
return this.handleConnectionClose() | ||
} | ||
} | ||
const sc = this.subscriptionContexts.get(data.id) | ||
@@ -212,0 +239,0 @@ if (sc) { |
@@ -23,3 +23,4 @@ 'use strict' | ||
let context = { | ||
app: fastify | ||
app: fastify, | ||
pubsub: subscriber | ||
} | ||
@@ -26,0 +27,0 @@ |
{ | ||
"name": "mercurius", | ||
"version": "7.1.0", | ||
"version": "7.2.0", | ||
"description": "Fastify GraphQL adapter with gateway and subscription support", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
# mercurius | ||
![CI workflow](https://github.com/fastify/fastify-oauth2/workflows/CI%20workflow/badge.svg) | ||
![CI workflow](https://github.com/mercurius-js/mercurius/workflows/CI%20workflow/badge.svg) | ||
@@ -5,0 +5,0 @@ Mercurius is a [**GraphQL**](https://graphql.org/) adapter for [**Fastify**](https://www.fastify.io) |
@@ -955,2 +955,66 @@ 'use strict' | ||
test('gateway - preGatewayExecution hooks should contain service metadata', async (t) => { | ||
t.plan(21) | ||
const app = await createTestGatewayServer(t) | ||
// Execution events: | ||
// - user service: once for user service query | ||
// - post service: once for post service query | ||
// - post service: once for reference type topPosts on User | ||
// - user service: once for reference type author on Post | ||
app.graphql.addHook('preGatewayExecution', async function (schema, document, context, service) { | ||
await immediate() | ||
t.type(schema, GraphQLSchema) | ||
t.type(document, 'object') | ||
t.type(context, 'object') | ||
if (typeof service === 'object' && service.name === 'user') { | ||
t.is(service.name, 'user') | ||
} else if (typeof service === 'object' && service.name === 'post') { | ||
t.is(service.name, 'post') | ||
} else { | ||
t.fail('service metadata should be correctly populated') | ||
return | ||
} | ||
t.ok('preGatewayExecution called') | ||
}) | ||
const res = await app.inject({ | ||
method: 'POST', | ||
headers: { 'content-type': 'application/json' }, | ||
url: '/graphql', | ||
body: JSON.stringify({ query }) | ||
}) | ||
t.deepEqual(JSON.parse(res.body), { | ||
data: { | ||
me: { | ||
id: 'u1', | ||
name: 'John', | ||
topPosts: [ | ||
{ | ||
pid: 'p1', | ||
author: { | ||
id: 'u1' | ||
} | ||
}, | ||
{ | ||
pid: 'p3', | ||
author: { | ||
id: 'u1' | ||
} | ||
} | ||
] | ||
}, | ||
topPosts: [ | ||
{ | ||
pid: 'p1' | ||
}, | ||
{ | ||
pid: 'p2' | ||
} | ||
] | ||
} | ||
}) | ||
}) | ||
// ------------- | ||
@@ -957,0 +1021,0 @@ // onResolution |
@@ -55,3 +55,2 @@ /* eslint-disable no-unused-expressions */ | ||
request: FastifyRequest | ||
reply: FastifyReply | ||
} | ||
@@ -78,6 +77,5 @@ } | ||
cache: true, | ||
context: (request, reply) => { | ||
context: (request) => { | ||
return { | ||
request, | ||
reply | ||
request | ||
} | ||
@@ -138,2 +136,6 @@ }, | ||
// all params are optional | ||
const opts: MercuriusOptions = {} | ||
app.register(mercurius, opts) | ||
app.register(async function (app) { | ||
@@ -501,1 +503,30 @@ app.graphql.extendSchema(` | ||
}) | ||
app.graphql.addHook('preSubscriptionParsing', async function (schema, source, context) { | ||
console.log('preSubscriptionParsing called') | ||
}) | ||
app.graphql.addHook('preSubscriptionExecution', async function (schema, document, context) { | ||
console.log('preSubscriptionExecution called') | ||
}) | ||
app.graphql.addHook('preGatewaySubscriptionExecution', async function (schema, document, context) { | ||
console.log('preGatewaySubscriptionExecution called') | ||
}) | ||
app.graphql.addHook('onSubscriptionResolution', async function (execution, context) { | ||
console.log('onSubscriptionResolution called') | ||
}) | ||
app.graphql.addHook('onSubscriptionEnd', async function (context) { | ||
console.log('onSubscriptionEnd called') | ||
}) | ||
// Hooks containing service metadata | ||
app.graphql.addHook('preGatewayExecution', async function (schema, document, context, service) { | ||
console.log('preGatewayExecution called') | ||
}) | ||
app.graphql.addHook('preGatewaySubscriptionExecution', async function (schema, document, context, service) { | ||
console.log('preGatewaySubscriptionExecution called') | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
675689
102
23380