Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mercurius

Package Overview
Dependencies
Maintainers
2
Versions
116
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mercurius - npm Package Compare versions

Comparing version 13.0.0 to 13.1.0

docs/custom-directive.md

12

docs/hooks.md

@@ -203,4 +203,2 @@ # Hooks

There is one hook that you can use in a GraphQL application.
When registering hooks, you must wait for Mercurius to be registered in Fastify.

@@ -211,1 +209,11 @@

```
### onExtendSchema
This hook will be triggered when `extendSchema` is called. It receives the new schema and context object.
```js
app.graphql.addHook('onExtendSchema', async (schema, context) => {
await asyncMethod()
})
```

82

docsify/sidebar.md

@@ -1,41 +0,41 @@

* [**Home**](/)
* [Install](/#install)
* [Quick Start](/#quick-start)
* [Examples](/#examples)
* [Acknowledgements](/#acknowledgements)
* [License](/#license)
* [API](/docs/api/options)
* [Plugin Options](/docs/api/options#plugin-options)
* [HTTP Endpoints](/docs/api/options#http-endpoints)
* [Decorators](/docs/api/options#decorators)
* [Error Extensions](/docs/api/options#errors)
* [Context](/docs/context)
* [Loaders](/docs/loaders)
* [Hooks](/docs/hooks)
* [Lifecycle](/docs/lifecycle)
* [Federation](/docs/federation)
* [Subscriptions](/docs/subscriptions)
* [Batched Queries](/docs/batched-queries)
* [Persisted Queries](/docs/persisted-queries)
* [TypeScript Usage](/docs/typescript)
* [HTTP](/docs/http)
* [GraphQL over WebSocket](/docs/graphql-over-websocket.md)
* [Integrations](/docs/integrations/)
* [nexus](/docs/integrations/nexus)
* [TypeGraphQL](/docs/integrations/type-graphql)
* [Prisma](/docs/integrations/prisma)
* [Testing](/docs/integrations/mercurius-integration-testing)
* [Tracing - OpenTelemetry](/docs/integrations/open-telemetry)
* [NestJS](/docs/integrations/nestjs.md)
* [Related Plugins](/docs/plugins)
* [mercurius-auth](/docs/plugins#mercurius-auth)
* [mercurius-cache](/docs/plugins#mercurius-cache)
* [mercurius-validation](/docs/plugins#mercurius-validation)
* [mercurius-upload](/docs/plugins#mercurius-upload)
* [altair-fastify-plugin](/docs/plugins#altair-fastify-plugin)
* [mercurius-apollo-registry](/docs/plugins#mercurius-apollo-registry)
* [mercurius-apollo-tracing](/docs/plugins#mercurius-apollo-tracing)
* [Development](/docs/development)
* [Faq](/docs/faq)
* [Contribute](/docs/contribute)
- [**Home**](/)
- [Install](/#install)
- [Quick Start](/#quick-start)
- [Examples](/#examples)
- [Acknowledgements](/#acknowledgements)
- [License](/#license)
- [API](/docs/api/options)
- [Plugin Options](/docs/api/options#plugin-options)
- [HTTP Endpoints](/docs/api/options#http-endpoints)
- [Decorators](/docs/api/options#decorators)
- [Error Extensions](/docs/api/options#errors)
- [Context](/docs/context)
- [Loaders](/docs/loaders)
- [Hooks](/docs/hooks)
- [Lifecycle](/docs/lifecycle)
- [Federation](/docs/federation)
- [Subscriptions](/docs/subscriptions)
- [Custom directives](/docs/custom-directive.md)
- [Batched Queries](/docs/batched-queries)
- [Persisted Queries](/docs/persisted-queries)
- [TypeScript Usage](/docs/typescript)
- [HTTP](/docs/http)
- [GraphQL over WebSocket](/docs/graphql-over-websocket.md)
- [Integrations](/docs/integrations/)
- [nexus](/docs/integrations/nexus)
- [TypeGraphQL](/docs/integrations/type-graphql)
- [Prisma](/docs/integrations/prisma)
- [Testing](/docs/integrations/mercurius-integration-testing)
- [Tracing - OpenTelemetry](/docs/integrations/open-telemetry)
- [NestJS](/docs/integrations/nestjs.md)
- [Related Plugins](/docs/plugins)
- [mercurius-auth](/docs/plugins#mercurius-auth)
- [mercurius-cache](/docs/plugins#mercurius-cache)
- [mercurius-validation](/docs/plugins#mercurius-validation)
- [mercurius-upload](/docs/plugins#mercurius-upload)
- [altair-fastify-plugin](/docs/plugins#altair-fastify-plugin)
- [mercurius-apollo-registry](/docs/plugins#mercurius-apollo-registry)
- [mercurius-apollo-tracing](/docs/plugins#mercurius-apollo-tracing)
- [Development](/docs/development)
- [Faq](/docs/faq)
- [Contribute](/docs/contribute)

@@ -178,2 +178,9 @@ import {

export interface onExtendSchemaHandler<TContext = MercuriusContext> {
(
schema: GraphQLSchema,
context: TContext,
): Promise<void> | void;
}
interface ServiceConfig {

@@ -202,3 +209,3 @@ setSchema: (schema: string) => ServiceConfig;

*/
extendSchema(schema: string | Source | DocumentNode): void;
extendSchema(schema: string | Source | DocumentNode): Promise<void> | void;
/**

@@ -285,2 +292,3 @@ * Define additional resolvers

// Application lifecycle addHooks
addHook<TContext = MercuriusContext>(name: 'onExtendSchema', hook: onExtendSchemaHandler<TContext>): void;
}

@@ -287,0 +295,0 @@

@@ -39,3 +39,3 @@ 'use strict'

} = require('./lib/errors')
const { Hooks, assignLifeCycleHooksToContext } = require('./lib/hooks')
const { Hooks, assignLifeCycleHooksToContext, assignApplicationHooksToContext } = require('./lib/hooks')
const { kLoaders, kFactory, kSubscriptionFactory, kHooks } = require('./lib/symbols')

@@ -46,3 +46,4 @@ const {

preExecutionHandler,
onResolutionHandler
onResolutionHandler,
onExtendSchemaHandler
} = require('./lib/handlers')

@@ -153,2 +154,6 @@

if (Array.isArray(schema)) {
if (schema.some(s => typeof s !== 'string')) {
throw new MER_ERR_INVALID_OPTS('when providing an array to the "schema" option, only string schemas are allowed')
}
schema = schema.join('\n')

@@ -253,2 +258,8 @@ }

fastifyGraphQl.schema = extendSchema(fastifyGraphQl.schema, s)
const context = assignApplicationHooksToContext({}, fastifyGraphQl[kHooks])
if (context.onExtendSchema !== null) {
return onExtendSchemaHandler({ schema: fastifyGraphQl.schema, context })
}
}

@@ -255,0 +266,0 @@

@@ -91,2 +91,10 @@ 'use strict'

async function onExtendSchemaHandler (request) {
await hooksRunner(
request.context.onExtendSchema,
hookRunner,
request
)
}
module.exports = {

@@ -100,3 +108,4 @@ preParsingHandler,

onSubscriptionResolutionHandler,
onSubscriptionEndHandler
onSubscriptionEndHandler,
onExtendSchemaHandler
}
'use strict'
const applicationHooks = [
'onExtendSchema'
]
const lifecycleHooks = [

@@ -27,2 +29,3 @@ 'preParsing',

this.onSubscriptionEnd = []
this.onExtendSchema = []
}

@@ -65,2 +68,10 @@

function assignApplicationHooksToContext (context, hooks) {
const contextHooks = {
onExtendSchema: null
}
if (hooks.onExtendSchema.length > 0) contextHooks.onExtendSchema = hooks.onExtendSchema.slice()
return Object.assign(context, contextHooks)
}
async function hooksRunner (functions, runner, request) {

@@ -124,2 +135,3 @@ for (const fn of functions) {

assignLifeCycleHooksToContext,
assignApplicationHooksToContext,
hooksRunner,

@@ -126,0 +138,0 @@ preExecutionHooksRunner,

{
"name": "mercurius",
"version": "13.0.0",
"version": "13.1.0",
"description": "Fastify GraphQL adapter with subscription support",

@@ -33,8 +33,8 @@ "main": "index.js",

"devDependencies": {
"@graphql-tools/merge": "^8.0.0",
"@graphql-tools/schema": "^9.0.2",
"@graphql-tools/utils": "^9.1.4",
"@sinonjs/fake-timers": "^10.0.2",
"@graphql-tools/merge": "^9.0.0",
"@graphql-tools/schema": "^10.0.0",
"@graphql-tools/utils": "^10.0.0",
"@sinonjs/fake-timers": "^11.0.0",
"@types/isomorphic-form-data": "^2.0.0",
"@types/node": "^18.0.0",
"@types/node": "^20.1.4",
"@types/ws": "^8.2.0",

@@ -48,2 +48,3 @@ "@typescript-eslint/eslint-plugin": "^5.21.0",

"graphql-ws": "^5.11.2",
"graphql": "^16.0.0",
"pre-commit": "^1.2.2",

@@ -66,3 +67,2 @@ "proxyquire": "^2.1.3",

"fastify-plugin": "^4.2.0",
"graphql": "^16.0.0",
"graphql-jit": "^0.8.0",

@@ -69,0 +69,0 @@ "mqemitter": "^5.0.0",

@@ -249,7 +249,4 @@ 'use strict'

query = '{ subtract(x: 4, y: 2) }'
try {
await app.graphql(query)
} catch (err) {
t.equal(err.errors[0].message, 'Cannot query field "subtract" on type "Query".')
}
await t.rejects(app.graphql(query), { errors: [{ message: 'Cannot query field "subtract" on type "Query".' }] })
})

@@ -308,7 +305,4 @@

query = '{ subtract(x: 4, y: 2) }'
try {
await app.graphql(query)
} catch (err) {
t.equal(err.errors[0].message, 'Cannot query field "subtract" on type "Query".')
}
await t.rejects(app.graphql(query), { errors: [{ message: 'Cannot query field "subtract" on type "Query".' }] })
})

@@ -338,7 +332,3 @@

try {
await app.ready()
} catch (error) {
t.equal(error.message, 'Invalid options: Must provide valid Document AST')
}
await t.rejects(app.ready(), 'Invalid options: Must provide valid Document AST')
})

@@ -1009,7 +999,3 @@

try {
await app.ready()
} catch (error) {
t.equal(error.message, 'Invalid options: Must provide valid Document AST')
}
await t.rejects(app.ready(), { message: 'Invalid options: Must provide valid Document AST' })
})

@@ -1029,7 +1015,3 @@

try {
await app.ready()
} catch (error) {
t.equal(error.message, 'Invalid options: Must provide valid Document AST')
}
await t.rejects(app.ready(), { message: 'Invalid options: Must provide valid Document AST' })
})

@@ -1083,2 +1065,25 @@

test('array of schemas contains a non-string schema', async t => {
const app = Fastify()
app.register(GQL, {
schema: [makeExecutableSchema({
typeDefs: `
type Query {
add(x: Int, y: Int): Int
}
`,
resolvers: {
Query: {
add: async (_, { x, y }) => x + y
}
}
})]
})
await t.rejects(app.ready(), {
message: 'Invalid options: when providing an array to the "schema" option, only string schemas are allowed'
})
})
test('Error in schema', async (t) => {

@@ -1104,12 +1109,10 @@ const schema = `

try {
app.register(GQL, {
schema,
resolvers
})
await app.ready()
} catch (error) {
t.equal(error.message, 'Interface field Event.Id expected but CustomEvent does not provide it.')
t.equal(error.name, 'GraphQLError')
}
app.register(GQL, {
schema,
resolvers
})
await t.rejects(app.ready(), {
message: 'Interface field Event.Id expected but CustomEvent does not provide it.',
name: 'GraphQLError'
})
})

@@ -1141,16 +1144,20 @@

try {
app.register(GQL, {
schema,
resolvers
})
await app.ready()
} catch (error) {
t.equal(error.message, 'Invalid schema: check out the .errors property on the Error')
t.equal(error.name, 'FastifyError')
t.equal(error.errors[0].message, 'Interface field Event.Id expected but CustomEvent does not provide it.')
t.equal(error.errors[0].name, 'GraphQLError')
t.equal(error.errors[1].message, 'Interface field Event.Id expected but AnotherEvent does not provide it.')
t.equal(error.errors[1].name, 'GraphQLError')
}
app.register(GQL, {
schema,
resolvers
})
await t.rejects(app.ready(), {
message: 'Invalid schema: check out the .errors property on the Error',
name: 'FastifyError',
errors: [
{
message: 'Interface field Event.Id expected but CustomEvent does not provide it.',
name: 'GraphQLError'
},
{
message: 'Interface field Event.Id expected but AnotherEvent does not provide it.',
name: 'GraphQLError'
}
]
})
})

@@ -1330,8 +1337,6 @@

}`
try {
await app.graphql(query)
} catch (err) {
t.equal(err.message, 'Invalid AST Node: { definitions: [[Object]] }.')
t.equal(err.name, 'Error')
}
await t.rejects(app.graphql(query), {
message: 'Invalid AST Node: { definitions: [[Object]] }.',
name: 'Error'
})
})

@@ -1338,0 +1343,0 @@

@@ -37,9 +37,3 @@ 'use strict'

await app.ready()
try {
await app.graphql(query)
} catch (e) {
t.equal(e.errors.length > 0, true)
t.equal(e.errors[0].message, 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".')
}
await t.rejects(app.graphql(query), { errors: [{ message: 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".' }] })
})

@@ -61,9 +55,3 @@

await app.ready()
try {
await app.graphql(query)
} catch (e) {
t.equal(e.errors.length > 0, true)
t.equal(e.errors[0].message, 'GraphQL introspection has been disabled, but the requested query contained the field "__type".')
}
await t.rejects(app.graphql(query), { errors: [{ message: 'GraphQL introspection has been disabled, but the requested query contained the field "__type".' }] })
})
'use strict'
const { test } = require('tap')
const sinon = require('sinon')
const Fastify = require('fastify')

@@ -129,34 +130,20 @@ const proxyquire = require('proxyquire')

test('hooks validation should handle invalid hook names', async t => {
t.plan(1)
const app = await createTestServer(t)
try {
app.graphql.addHook('unsupportedHook', async () => {})
} catch (e) {
t.equal(e.message, 'unsupportedHook hook not supported!')
}
t.rejects(async () => app.graphql.addHook('unsupportedHook', async () => {}), { message: 'unsupportedHook hook not supported!' })
})
test('hooks validation should handle invalid hook name types', async t => {
t.plan(2)
const app = await createTestServer(t)
try {
app.graphql.addHook(1, async () => {})
} catch (e) {
t.equal(e.code, 'MER_ERR_HOOK_INVALID_TYPE')
t.equal(e.message, 'The hook name must be a string')
}
t.rejects(async () => app.graphql.addHook(1, async () => {}), {
code: 'MER_ERR_HOOK_INVALID_TYPE',
message: 'The hook name must be a string'
})
})
test('hooks validation should handle invalid hook handlers', async t => {
t.plan(2)
const app = await createTestServer(t)
try {
app.graphql.addHook('preParsing', 'not a function')
} catch (e) {
t.equal(e.code, 'MER_ERR_HOOK_INVALID_HANDLER')
t.equal(e.message, 'The hook callback must be a function')
}
t.rejects(async () => app.graphql.addHook('preParsing', 'not a function'), {
code: 'MER_ERR_HOOK_INVALID_HANDLER',
message: 'The hook callback must be a function'
})
})

@@ -575,8 +562,8 @@

const query = `{
__type(name:"Query") {
const query = `{
__type(name:"Query") {
name
fields {
name
}
}
}

@@ -950,1 +937,40 @@ }`

})
test('onExtendSchema hooks should be triggered when extendSchema is called', async t => {
t.plan(1)
const app = await createTestServer(t)
app.graphql.addHook('onExtendSchema', async (schema, context) => {
t.pass('onExtendSchema called')
})
const extendedSchema = `
extend type Query {
sub(x: Int, y: Int): Int
}
`
const extendedResolvers = {
Query: {
sub: async (_, { x, y }) => x - y
}
}
await app.register(async function (app) {
app.graphql.extendSchema(extendedSchema)
app.graphql.defineResolvers(extendedResolvers)
})
})
test('onExtendSchema hooks should not be triggered if extendSchema is not called', async t => {
const onExtendSchemaFn = sinon.stub()
const app = await createTestServer(t)
app.graphql.addHook('onExtendSchema', async (schema, context) => {
onExtendSchemaFn()
})
sinon.assert.notCalled(onExtendSchemaFn)
})

@@ -34,7 +34,3 @@ 'use strict'

try {
await hooksRunner([fn1, fn2, fn3], iterator, 'a')
} catch (err) {
t.equal(err.message, 'kaboom')
}
await t.rejects(hooksRunner([fn1, fn2, fn3], iterator, 'a'), { message: 'kaboom' })

@@ -122,10 +118,4 @@ function iterator (fn, a) {

test('hooksRunner - Should handle when iterator errors', async (t) => {
t.plan(1)
await t.rejects(hooksRunner([fn1, fn2], iterator, 'a'), { message: 'kaboom' })
try {
await hooksRunner([fn1, fn2], iterator, 'a')
} catch (err) {
t.equal(err.message, 'kaboom')
}
function iterator (fn) {

@@ -177,9 +167,4 @@ throw new Error('kaboom')

const originalRequest = { schema: 'schema', document: 'document', context: 'context' }
await t.rejects(preExecutionHooksRunner([fn1, fn2, fn3], originalRequest), { message: 'kaboom' })
try {
await preExecutionHooksRunner([fn1, fn2, fn3], originalRequest)
} catch (err) {
t.equal(err.message, 'kaboom')
}
function fn1 (schema, document, context) {

@@ -275,10 +260,5 @@ t.equal(schema, 'schema')

const originalRequest = { schema: 'schema', document: 'document', context: 'context' }
await t.rejects(preExecutionHooksRunner([fn1, fn2, fn3], originalRequest), { message: 'kaboom' })
t.same(originalRequest, { schema: 'schema', document: 'document', context: 'context' })
try {
await preExecutionHooksRunner([fn1, fn2, fn3], originalRequest)
} catch (err) {
t.equal(err.message, 'kaboom')
t.same(originalRequest, { schema: 'schema', document: 'document', context: 'context' })
}
function fn1 (schema, document, context) {

@@ -285,0 +265,0 @@ t.equal(schema, 'schema')

@@ -451,9 +451,6 @@ 'use strict'

try {
// needed so that graphql is defined
await t.rejects(async () => {
await app.ready()
app.graphql('query { test }')
} catch (error) {
t.equal(error.message, 'Invalid options: Cannot find type test')
}
}, { message: 'Invalid options: Cannot find type test' })
})

@@ -468,7 +465,3 @@

try {
await app.ready()
} catch (error) {
t.equal(error.message, 'Invalid options: the jit option must be a number')
}
await t.rejects(app.ready(), { message: 'Invalid options: the jit option must be a number' })
})

@@ -487,17 +480,2 @@

test('options cache is boolean', async t => {
const app = Fastify()
app.register(GQL, {
cache: true,
schema
})
try {
await app.ready()
} catch (error) {
t.equal(error.message, 'Invalid options: Cache type is not supported')
}
})
test('options cache is !number && !boolean', async t => {

@@ -510,7 +488,3 @@ const app = Fastify()

try {
await app.ready()
} catch (error) {
t.equal(error.message, 'Invalid options: Cache type is not supported')
}
await t.rejects(app.ready(), { message: 'Invalid options: Cache type is not supported' })
})

@@ -528,9 +502,3 @@

await app.ready()
try {
await app.graphql('{ dogs { name { owner } } }')
} catch (error) {
t.equal(error.message, 'Graphql validation error')
t.end()
}
await t.rejects(app.graphql('{ dogs { name { owner } } }'), { message: 'Graphql validation error' })
})

@@ -537,0 +505,0 @@

@@ -159,3 +159,2 @@ 'use strict'

test('queryDepth - test total depth is over queryDepth parameter', async (t) => {
t.plan(1)
const app = Fastify()

@@ -176,7 +175,3 @@

try {
await app.graphql(query)
} catch (error) {
t.same(error, err)
}
await t.rejects(app.graphql(query), err)
})

@@ -183,0 +178,0 @@

@@ -14,2 +14,3 @@ 'use strict'

`
t.teardown(app.close.bind(app))

@@ -44,2 +45,3 @@ const resolvers = {

const app = Fastify()
t.teardown(app.close.bind(app))
const schema = `

@@ -76,5 +78,2 @@ type Query {

// needed so that graphql is defined
await app.ready()
const res = await app.inject({

@@ -96,2 +95,3 @@ method: 'GET',

const app = Fastify()
t.teardown(app.close.bind(app))
const schema = `

@@ -129,3 +129,16 @@ type Query {

t.equal(res.statusCode, 400)
t.matchSnapshot(JSON.stringify(JSON.parse(res.body)))
t.same(res.json(), {
errors: [
{
message: 'Syntax Error: Expected Name, found <EOF>.',
locations: [
{
line: 1,
column: 18
}
]
}
]
})
})

@@ -135,2 +148,3 @@

const app = Fastify()
t.teardown(app.close.bind(app))
const schema = `

@@ -183,3 +197,7 @@ type Query {

t.matchSnapshot(JSON.stringify(JSON.parse(res.body)))
t.same(res.json(), {
data: {
multiply: 25
}
})
})

@@ -70,3 +70,2 @@ const { test } = require('tap')

test('subscription context publish event returns a promise reject on error', async t => {
t.plan(1)
const emitter = mq()

@@ -79,7 +78,3 @@ const error = new Error('Dummy error')

try {
await sc.subscribe('TOPIC')
} catch (e) {
t.same(error, e)
}
await t.rejects(sc.subscribe('TOPIC'), error)
})

@@ -86,0 +81,0 @@

@@ -26,3 +26,2 @@ 'use strict'

test('validationRules array - reports an error', async (t) => {
t.plan(2)
const app = Fastify()

@@ -47,9 +46,3 @@

await app.ready()
try {
await app.graphql(query)
} catch (e) {
t.equal(e.errors.length, 1)
t.equal(e.errors[0].message, 'Validation rule error')
}
await t.rejects(app.graphql(query), { errors: [{ message: 'Validation rule error' }] })
})

@@ -101,3 +94,2 @@

test('validationRules - reports an error', async (t) => {
t.plan(2)
const app = Fastify()

@@ -123,9 +115,3 @@

await app.ready()
try {
await app.graphql(query)
} catch (e) {
t.equal(e.errors.length, 1)
t.equal(e.errors[0].message, 'Validation rule error')
}
await t.rejects(app.graphql(query), { errors: [{ message: 'Validation rule error' }] })
})

@@ -234,3 +220,2 @@

test('validationRules - errors if cache is used with the function', async (t) => {
t.plan(1)
const app = Fastify()

@@ -246,8 +231,3 @@

// needed so that graphql is defined
try {
await app.ready()
} catch (e) {
t.equal(e.message, 'Invalid options: Using a function for the validationRules is incompatible with query caching')
}
await t.rejects(app.ready(), { message: 'Invalid options: Using a function for the validationRules is incompatible with query caching' })
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc