Schema Transformation
Transforming a given GraphQLSchema
can include the following activities:
- Hiding certain operations from the schema
- Hooking into the schema's resolver functions to...
- ... overwrite arguments
- ... overwrite resolved data
graphql-transform-schema
is a library that let's you perform these operations. The remainder of this chapter is about its API.
Hiding operations
Consider the following schema to start with. It exposes CRUD operations for a User
type:
type Query {
user(id: ID!): User!
allUsers: [User!]
}
type Mutation {
createUser(name: String!): User
updateUser(id: ID!, name: String!): User
deleteUser(id: ID!): User
}
type User {
id: ID!
name: String!
}
Assume there is now a requirement to hide the allUsers
query as well as the deleteUser
mutation, i.e. not expose it through the API any more. graphql-transform-schema
offers the following way to implement this scenario:
const schema = ... // this is the GraphQLSchema instance implementing the SDL from above
const transformedSchema = transformSchema(schema, {
Query: {
'allUsers': false // hide `allUsers` field on `Query` type
},
Mutation: {
'deleteUser': false // hide `deleteUser` field on `Mutation` type
}
})
As you can see, you can call the transformSchema
function and pass in the original schema along with a set of rules. Each rule refers to a field on one of the GraphQL types defined in the schema and specifies whether it should be visible or not by providing a boolean value.
Note that it is currently not possible to provide a function which returns a boolean value instead of the boolean value directly.
The resulting transformedSchema
now doesn't expose the allUsers
query and deleteUser
mutation any more. Written in the SDL, the schema would now look as follows:
type Query {
user(id: ID!): User!
}
type Mutation {
createUser(name: String!): User
updateUser(id: ID!, name: String!): User
}
type User {
id: ID!
name: String!
}
Note that transformSchema
also supports rules that are using a wildcard to refer to certain a group of fields. For example, if you have multiple mutation fields beginning with delete
(e.g. deleteUser
and deleteArticle
), you could hide all of these mutations as follows:
const transformedSchema = transformSchema(schema, {
Mutation: {
'delete*': false // hide all mutations beginning with `delete`
}
})
Hooking into resolver functions
Instead of simply setting a boolean value on a field in the rules that are passed to transformSchema
, it's also possible to provide a function that hooks into the field's actual resolver.
This allows for two main features:
- Overwrite the arguments for a specific field when its resolver is invoked
- Overwrite the result after the resolver for a specific field was invoked
Consider the following schema exposing only a single query called hello
:
type Query {
hello(name: String!): String!
}
Here's the corresponding resolver:
{
Query: {
hello: (_, args) => {
return `Hello ${args.name}`
}
}
}
Now, using transformSchema
you can either modify the name
argument that's passed into the resolver function, or the resolver's return value (or both).
Modifying resolver arguments
Say you want to modify the name
argument and always capitalize the whole word. Here is how this requirement can be implemented:
const schema = ... // this is the GraphQLSchema instance implementing the SDL from above
const transformedSchema = transformSchema(schema, {
Query: {
'hello': ({args, resolve}) => {
const uppercaseName = args.name.toUpperCase()
return resolve({name: uppercaseName})
}
}
})
Instead of passing a rule that specifies whether or not to hide the hello
field, this time a function is used to hook into the resolver function of hello
. The function receives as input parameters the args
provided for the query (in this case the optional name
specified in the schema above) and the actual resolve
function. That function is now responsible to actually call the resolver - but before doing so it's possible to modify the input arguments, which is precisely what's happening here.
Assume the following query is executed against the schema:
query {
hello(name: "Alice")
}
This is what the response will look like:
{
"data": {
"hello": "Hello ALICE"
}
}
Modifying resolver return values
transformSchema
also allows to modify the returned data using the same API. Assuming you want to capitalize the whole string that's returned by the resolver, here is the implementation:
const schema = ... // this is the GraphQLSchema instance implementing the SDL from above
const transformedSchema = transformSchema(schema, {
Query: {
'hello': ({args, resolve}) => {
return resolve(args).toUpperCase()
}
}
})
This example is similar to the previous one, except that this time it's not the name
argument that's transformed to uppercase upfront, but the whole return value instead.
Running the same query from above:
query {
hello(name: "Alice")
}
This is going to yield the following response this time:
{
"data": {
"hello": "HELLO ALICE"
}
}