Skip to content

Commit

Permalink
Merge pull request #979 from brainrepo/docs/913-schema-per-http-header
Browse files Browse the repository at this point in the history
docs: document schema by http header (#913)
  • Loading branch information
simoneb committed Apr 24, 2023
2 parents b6ac057 + 011a8e7 commit daa6462
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 0 deletions.
122 changes: 122 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,125 @@ app.register(mercurius, {
validationRules: process.env.NODE_ENV === 'production' && [NoSchemaIntrospectionCustomRule],
});
```

## Execute against different schemas based on request headers

Sometimes we may face the need to present a scheme that varies depending on specific situations.
To accomplish this need we can use one powerful fastify/find-my-way feature called **Custom Constraints**.

https://www.fastify.io/docs/latest/Reference/Routes/#asynchronous-custom-constraints

> Fastify supports constraining routes to match only certain requests based on some property of the request, like the Host header, or any other value via find-my-way constraints.
We can then create two mercurius instances that expose the two different schemas and use the constraint on the header to drive the request to one or other mercurius instance.

### 1. Create the constraint and initialize the fastify instance
```js
const Fastify = require('fastify')
const mercurius = require('..')

// Define the constraint custom strategy
const schemaStrategy = {
name: 'schema',
storage: function () {
const handlers = {}
return {
get: (type) => { return handlers[type] || null },
set: (type, store) => { handlers[type] = store }
}
},
deriveConstraint: (req, ctx) => {
return req.headers.schema
},
validate: () => true,
mustMatchWhenDerived: true
}

// Initialize fastify
const app = Fastify({ constraints: { schema: schemaStrategy } })
```
### 2. Initialize the first mercurius instance and bind it to the `/` route only if the `schema` header value is equal to `A`

```js
const schema = `
type Query {
add(x: Int, y: Int): Int
}
`

const resolvers = {
Query: {
add: async (_, obj) => {
const { x, y } = obj
return x + y
}
}
}

// Schema A registration with A constraint
app.register(async childServer => {
childServer.register(mercurius, {
schema,
resolvers,
graphiql: false,
routes: false
})

childServer.route({
path: '/',
method: 'POST',
constraints: { schema: 'A' },
handler: (req, reply) => {
const query = req.body
return reply.graphql(query)
}
})
})
```
### 3. Initialize the second mercurius instance and bind it to the `/` route only if the `schema` header value is equal to `B`

```js
const schema2 = `
type Query {
subtract(x: Int, y: Int): Int
}
`

const resolvers2 = {
Query: {
subtract: async (_, obj) => {
const { x, y } = obj
return x - y
}
}
}

app.register(async childServer => {
childServer.register(mercurius, {
schema: schema2,
resolvers: resolvers2,
graphiql: false,
routes: false
})

childServer.route({
path: '/',
method: 'POST',
constraints: { schema: 'B' },
handler: (req, reply) => {
const query = req.body
return reply.graphql(query)
}
})
})
```

4. Start the fastify server

```js
app.listen({ port: 3000 })
```

### Important notes:

In order to use graphql in constrained routes we need to set mercurius `routes` parameter to `false` in order to avoid that both the mercurius instances try to expose themself at `/graphql`.
96 changes: 96 additions & 0 deletions examples/schema-by-http-header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict'

const Fastify = require('fastify')
const mercurius = require('..')

// Define the constraint custom strategy
const schemaStrategy = {
name: 'schema',
storage: function () {
const handlers = {}
return {
get: (type) => { return handlers[type] || null },
set: (type, store) => { handlers[type] = store }
}
},
deriveConstraint: (req, ctx) => {
return req.headers.schema
},
validate: () => true,
mustMatchWhenDerived: true
}

// Initialize fastify
const app = Fastify({ constraints: { schema: schemaStrategy } })

// Schema 1 definition
const schema = `
type Query {
add(x: Int, y: Int): Int
}
`

const resolvers = {
Query: {
add: async (_, obj) => {
const { x, y } = obj
return x + y
}
}
}

// Schema A registration with A constraint
app.register(async childServer => {
childServer.register(mercurius, {
schema,
resolvers,
graphiql: false,
routes: false
})

childServer.route({
path: '/',
method: 'POST',
constraints: { schema: 'A' },
handler: (req, reply) => {
const query = req.body
return reply.graphql(query)
}
})
})

const schema2 = `
type Query {
subtract(x: Int, y: Int): Int
}
`

const resolvers2 = {
Query: {
subtract: async (_, obj) => {
const { x, y } = obj
return x - y
}
}
}

app.register(async childServer => {
childServer.register(mercurius, {
schema: schema2,
resolvers: resolvers2,
graphiql: false,
routes: false
})

childServer.route({
path: '/',
method: 'POST',
constraints: { schema: 'B' },
handler: (req, reply) => {
const query = req.body
return reply.graphql(query)
}
})
})

app.listen({ port: 3000 })

0 comments on commit daa6462

Please sign in to comment.