Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add the ability to add in additional route properties #1080

Merged
merged 9 commits into from
Feb 16, 2024
1 change: 1 addition & 0 deletions docs/api/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
- `allowBatchedQueries`: Boolean. Flag to control whether to allow batched queries. When `true`, 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
- `additionalRouteProps`: Object. Takes similar configuration to the Route Options of Fastify. Usecases include being able to add constraints, modify validation schema, increase the `bodyLimit`, etc. You can read more about the possible values on [fastify.dev's Routes Options](https://fastify.dev/docs/latest/Reference/Routes/#options)
barelyhuman marked this conversation as resolved.
Show resolved Hide resolved

#### queryDepth example

Expand Down
13 changes: 13 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
FastifyReply,
FastifyRequest,
FastifyInstance,
RouteOptions
} from "fastify";
import {
DocumentNode,
Expand Down Expand Up @@ -487,6 +488,18 @@ declare namespace mercurius {
* receive an array of responses within a single request.
*/
allowBatchedQueries?: boolean;

/**
* Customize the graphql routers initialized by mercurius with
* more fastify route options.
*
* Usecases:
* - Add more validation
* - change the schema structure
* - Hook on the request's life cycle
* - Increase body size limit for larger queries
*/
additionalRouteProps?:Omit<RouteOptions,"handler" | "wsHandler">
barelyhuman marked this conversation as resolved.
Show resolved Hide resolved
}

export type MercuriusOptions = MercuriusCommonOptions & (MercuriusSchemaOptions)
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ const mercurius = fp(async function (app, opts) {
entityResolversFactory: undefined,
subscriptionContextFn,
keepAlive,
fullWsTransport
fullWsTransport,
additionalRouteProps: opts.additionalRouteProps
barelyhuman marked this conversation as resolved.
Show resolved Hide resolved
})
}

Expand Down
13 changes: 11 additions & 2 deletions lib/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ module.exports = async function (app, opts) {
persistedQueryProvider,
allowBatchedQueries,
keepAlive,
fullWsTransport
fullWsTransport,
additionalRouteProps
} = opts

// Load the persisted query settings
Expand All @@ -214,6 +215,12 @@ module.exports = async function (app, opts) {
notSupportedError
} = persistedQueryProvider || {}

const normalizedRouteProps = { ...additionalRouteProps }
if (normalizedRouteProps.handler || normalizedRouteProps.wsHandler) {
normalizedRouteProps.handler = undefined
normalizedRouteProps.wsHandler = undefined
}
barelyhuman marked this conversation as resolved.
Show resolved Hide resolved

async function executeQuery (query, variables, operationName, request, reply) {
// Validate a query is present
if (!query) {
Expand Down Expand Up @@ -293,6 +300,7 @@ module.exports = async function (app, opts) {
method: 'GET',
schema: getSchema,
attachValidation: true,
...normalizedRouteProps,
handler: async function (request, reply) {
// Generate the context for this request
if (contextFn) {
Expand Down Expand Up @@ -338,7 +346,8 @@ module.exports = async function (app, opts) {

app.post(graphqlPath, {
schema: postSchema(allowBatchedQueries),
attachValidation: true
attachValidation: true,
...normalizedRouteProps
}, async function (request, reply) {
// Generate the context for this request
if (contextFn) {
Expand Down
97 changes: 97 additions & 0 deletions test/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2187,3 +2187,100 @@ test('if ide has plugin set, serve config.js with the correct endpoint', async (
t.equal(res.body.toString(), 'window.GRAPHQL_ENDPOINT = \'/something/app/graphql\';\n' +
'window.GRAPHIQL_PLUGIN_LIST = []')
})

test('GET graphql endpoint with invalid additionalRouteProp constraint', async (t) => {
const app = Fastify()
const schema = `
type Query {
add(x: Int, y: Int): Int
}
`
app.register(GQL, {
schema,
additionalRouteProps: {
constraints: {
host: 'auth.fastify.dev'
}
}
})

const res = await app.inject({
method: 'GET',
url: '/graphiql',
headers: {
Host: 'fail.fastify.dev'
}
})

t.equal(res.statusCode, 404)
})

test('GET graphql endpoint with valid additionalRouteProp constraint', async (t) => {
const app = Fastify()
const schema = `
type Query {
add(x: Int, y: Int): Int
}
`
app.register(GQL, {
schema,
additionalRouteProps: {
constraints: {
host: 'auth.fastify.dev'
}
}
})

const query = '{ add(x: 2, y: 2) }'

const res = await app.inject({
method: 'GET',
url: '/graphql',
headers: {
Host: 'auth.fastify.dev',
'Content-Type': 'text/plain'
},
query: {
query
}
})

t.equal(res.statusCode, 200)
})

test('GET graphql endpoint, additionalRouteProps should ignore custom handler', async (t) => {
const app = Fastify()
const schema = `
type Query {
add(x: Int, y: Int): Int
}
`

let executed = false
simoneb marked this conversation as resolved.
Show resolved Hide resolved

app.register(GQL, {
schema,
additionalRouteProps: {
// @ts-expect-error `handler` property is ignored in props
handler (req, reply) {
executed = true
}
}
})

const query = '{ add(x: 2, y: 2) }'

const res = await app.inject({
method: 'GET',
url: '/graphql',
headers: {
'Content-Type': 'text/plain'
},
query: {
query
}
})

t.equal(res.statusCode, 200)
t.notOk(executed)
})
Loading