GraphQL
🕵️ Enable introspection
Enabling the introspection on your application
Using Apollo
When creating a new instance of the ApolloServer
, you have to provide an object describing your resolvers, and types definitions. This object can also include an introspection
parameter.
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
})
This option is documented in the ApolloServer
reference
Fine-tuning
Using Apollo plugins, you can also have a better access control over this query. Here is an example of plugin that prevents the access to the introspection query if the request does not feature the CLI header.
const secureIntrospection = {
requestDidStart: ({ request }) => {
if (
request.query.includes('__schema') ||
request.query.includes('__type')
) {
if (process.env.ESCAPE_IDENTIFIER) {
const introspectionSecureKeyHeader = request.http.headers.get(
'x-escape-identifier'
)
if (introspectionSecureKeyHeader !== process.env.ESCAPE_IDENTIFIER) {
throw new ForbiddenError('GraphQL introspection is disabled.')
}
}
}
},
}
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
plugins: [secureIntrospection],
})
The value of the variable ESCAPE_IDENTIFIER
could be contained in your environment or any secure storage you'd like.
Using NestJS
NestJS provides a GraphQL module through the @nestjs/graphql
package. The package is a wrapper around the Apollo GraphQL server, and it's usage is documented in the NestJS related documentation.
Starting from now, we will assume that you have a basic GraphQL module setup in your application like so:
@Module({
imports: [
...
GraphQLModule.forRoot({
autoSchemaFile: 'schema.gql'
}),
...
],
providers: []
})
export class AppModule {}
By default, the introspection is enabled as long as the NODE_ENV
is not production
. To change this behavior, you can just set the introspection
parameter to true
in the module declaration.
@Module({
imports: [
...
GraphQLModule.forRoot({
autoSchemaFile: 'schema.gql',
introspection: true
}),
...
],
providers: []
})
export class AppModule {}
For instance, you might want to manually define where to enable or not the introspection based on your environment. To do so, you can add a DISABLE_INTROSPECTION
env variable (provided by a .env
file or through an shell environment variable) and use it like so:
@Module({
imports: [
...
GraphQLModule.forRoot({
autoSchemaFile: 'schema.gql',
introspection: !process.env.DISABLE_INTROSPECTION
}),
...
],
providers: []
})
export class AppModule {}
This will enable introspection by default, and disable it in every environment you decide to.
Fine-tuning
Any of the options you pass to the GraphQLModule.forRoot
method is part of the Apollo configuration object. Thus, you can re-use the Apollo plugin method for a fine-tuned access control.
const secureIntrospection = ... // see the apollo technique documentation
@Module({
imports: [
...
GraphQLModule.forRoot({
autoSchemaFile: 'schema.gql',
introspection: !process.env.DISABLE_INTROSPECTION,
plugins: [secureIntrospection]
}),
...
],
providers: []
})
export class AppModule {}
Using Yoga
Yoga plugin system is built with Envelop. Refer to the Envelop section for more information.
Using Envelop
Envelop provides a plugin disable-introspection
that allows you to disable the introspection query. You can find more information about this plugin in the Envelop documentation.
import { envelop } from '@envelop/core'
import { useDisableIntrospection } from '@envelop/disable-introspection'
const getEnveloped = envelop({
plugins: [
useDisableIntrospection({
disableIf: ({ context }) => {
return (
context.request.headers.get('x-escape-identifier') !==
process.env.ESCAPE_IDENTIFIER
)
},
}),
],
})
Retrieving your introspection schema manually
GraphQL endpoints are introspectable, meaning that one can query an API to get details about the API itself. This feature is enabled by default, but you may have disabled it in production. Escape needs these introspection details to scan your API, and can work with either a schema file or an introspection result. Read on to learn how to get these files.
Retrieving a GraphQL schema
A GraphQL schema is the specification of your API. It defines the types of your API, the queries and mutations you can perform, and the relations between them. Depending on your development approach, it is either written by hand or generated from your code.
- If you are using a schema-first approach, there should be a
schema.graphql
orschema.gql
file committed in your repository. The name may vary depending on your configuration. - If you are using a code-first approach, you can have your framework save your schema to a file with configuration options like
autoSchemaFile
(NestJS) or similar.
Sending an introspection query
The introspection result is what a GraphQL endpoint answers to a query on the __schema
meta-field.
You can retrieve the introspection result by running the following query on your GraphQL endpoint. Save the result to a file named introspection-result.json
and upload it to Escape.
GraphQL introspection query (92 lines)
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
fields(includeDeprecated: true) {
name
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
You can do this with a simple curl
command:
curl --location 'https://example.com/graphql' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{"query":"query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } } }","variables":{}}' \
> introspection.json
REST
Escape is compatible with Swagger v2, OpenAPI v3 and Postman Collections.
More input sources will be available soon, as the ability to scan undocumented APIs.