2018-03-27 17:15:28 +02:00
'use strict' ;
/ * *
* Module dependencies
* /
// Public node modules.
2018-03-29 14:03:09 +02:00
const _ = require ( 'lodash' ) ;
2018-09-10 16:05:00 +08:00
const { ApolloServer } = require ( 'apollo-server-koa' ) ;
2018-05-25 16:08:51 +02:00
const depthLimit = require ( 'graphql-depth-limit' ) ;
2020-10-07 15:33:02 +02:00
const { graphqlUploadKoa } = require ( 'graphql-upload' ) ;
2019-04-09 21:51:28 +02:00
const loadConfigs = require ( './load-config' ) ;
2018-03-27 17:15:28 +02:00
2020-01-30 10:49:42 +01:00
const attachMetadataToResolvers = ( schema , { api , plugin } ) => {
const { resolver = { } } = schema ;
if ( _ . isEmpty ( resolver ) ) return schema ;
Object . keys ( resolver ) . forEach ( type => {
if ( ! _ . isPlainObject ( resolver [ type ] ) ) return ;
Object . keys ( resolver [ type ] ) . forEach ( resolverName => {
if ( ! _ . isPlainObject ( resolver [ type ] [ resolverName ] ) ) return ;
resolver [ type ] [ resolverName ] [ '_metadatas' ] = {
api ,
plugin ,
} ;
} ) ;
} ) ;
return schema ;
} ;
2018-03-27 17:15:28 +02:00
module . exports = strapi => {
2019-04-09 21:51:28 +02:00
const { appPath , installedPlugins } = strapi . config ;
2018-03-27 17:15:28 +02:00
return {
2019-11-04 11:29:19 +01:00
async beforeInitialize ( ) {
2018-03-28 20:13:09 +02:00
// Try to inject this hook just after the others hooks to skip the router processing.
2020-04-09 16:27:29 +02:00
if ( ! strapi . config . get ( 'hook.load.after' ) ) {
2018-04-02 18:33:12 +02:00
_ . set ( strapi . config . hook . load , 'after' , [ ] ) ;
}
strapi . config . hook . load . after . push ( 'graphql' ) ;
2018-03-30 17:05:24 +02:00
// Load core utils.
2019-04-09 21:51:28 +02:00
2019-06-10 20:37:13 +02:00
const { api , plugins , extensions } = await loadConfigs ( {
appPath ,
installedPlugins ,
} ) ;
_ . merge ( strapi , { api , plugins } ) ;
2018-03-31 18:55:08 +02:00
2021-04-07 12:06:32 +02:00
// Create a merge of all the GraphQL configuration.
2020-01-30 10:49:42 +01:00
const apisSchemas = Object . keys ( strapi . api || { } ) . map ( key => {
const schema = _ . get ( strapi . api [ key ] , 'config.schema.graphql' , { } ) ;
return attachMetadataToResolvers ( schema , { api : key } ) ;
} ) ;
2019-04-09 21:51:28 +02:00
2020-01-30 10:49:42 +01:00
const pluginsSchemas = Object . keys ( strapi . plugins || { } ) . map ( key => {
const schema = _ . get ( strapi . plugins [ key ] , 'config.schema.graphql' , { } ) ;
return attachMetadataToResolvers ( schema , { plugin : key } ) ;
} ) ;
2019-04-09 21:51:28 +02:00
2020-01-30 10:49:42 +01:00
const extensionsSchemas = Object . keys ( extensions || { } ) . map ( key => {
const schema = _ . get ( extensions [ key ] , 'config.schema.graphql' , { } ) ;
return attachMetadataToResolvers ( schema , { plugin : key } ) ;
} ) ;
2019-04-09 21:51:28 +02:00
2021-04-07 12:06:32 +02:00
const baseSchema = mergeSchemas ( [ ... pluginsSchemas , ... extensionsSchemas , ... apisSchemas ] ) ;
2019-06-10 20:37:13 +02:00
2019-04-09 21:51:28 +02:00
// save the final schema in the plugin's config
2021-04-07 12:06:32 +02:00
_ . set ( strapi . plugins . graphql , 'config._schema.graphql' , baseSchema ) ;
2018-03-27 17:15:28 +02:00
} ,
2019-11-04 11:29:19 +01:00
initialize ( ) {
2021-01-22 19:06:04 +01:00
const schema = strapi . plugins . graphql . services [ 'schema-generator' ] . generateSchema ( ) ;
2018-03-27 17:15:28 +02:00
2021-01-22 19:06:04 +01:00
if ( _ . isEmpty ( schema ) ) {
2020-03-02 15:18:08 +01:00
strapi . log . warn ( 'The GraphQL schema has not been generated because it is empty' ) ;
2018-03-29 14:03:09 +02:00
2019-08-14 14:15:45 +02:00
return ;
2018-03-29 14:03:09 +02:00
}
2020-09-04 09:51:11 +02:00
const config = _ . get ( strapi . plugins . graphql , 'config' , { } ) ;
// TODO: Remove these deprecated options in favor of `apolloServer` in the next major version
const deprecatedApolloServerConfig = {
tracing : _ . get ( config , 'tracing' , false ) ,
introspection : _ . get ( config , 'introspection' , true ) ,
engine : _ . get ( config , 'engine' , false ) ,
} ;
if ( [ 'tracing' , 'introspection' , 'engine' ] . some ( key => _ . has ( config , key ) ) ) {
strapi . log . warn (
'The `tracing`, `introspection` and `engine` options are deprecated in favor of the `apolloServer` object and they will be removed in the next major version.'
) ;
}
const apolloServerConfig = _ . get ( config , 'apolloServer' , { } ) ;
2018-09-10 16:05:00 +08:00
const serverParams = {
2021-01-22 19:06:04 +01:00
schema ,
2020-10-07 15:33:02 +02:00
uploads : false ,
2019-03-25 16:37:46 +01:00
context : ( { ctx } ) => {
// Initiliase loaders for this request.
2019-04-09 21:51:28 +02:00
// TODO: set loaders in the context not globally
2020-02-10 19:13:01 +01:00
strapi . plugins . graphql . services [ 'data-loaders' ] . initializeLoader ( ) ;
2019-03-25 16:37:46 +01:00
return {
context : ctx ,
} ;
} ,
2020-06-16 20:57:05 +09:00
formatError : err => {
2020-09-04 09:51:11 +02:00
const formatError = _ . get ( config , 'formatError' , null ) ;
2020-06-16 20:57:05 +09:00
return typeof formatError === 'function' ? formatError ( err ) : err ;
} ,
2020-09-04 09:51:11 +02:00
validationRules : [ depthLimit ( config . depthLimit ) ] ,
2018-09-10 16:05:00 +08:00
playground : false ,
2019-08-21 11:05:33 +02:00
cors : false ,
bodyParserConfig : true ,
2020-09-04 09:51:11 +02:00
// TODO: Remove these deprecated options in favor of `apolloServerConfig` in the next major version
... deprecatedApolloServerConfig ,
... apolloServerConfig ,
2018-09-10 16:05:00 +08:00
} ;
2018-03-27 17:15:28 +02:00
2018-05-15 16:03:22 +02:00
// Disable GraphQL Playground in production environment.
2020-09-04 09:51:11 +02:00
if ( strapi . config . environment !== 'production' || config . playgroundAlways ) {
2018-09-10 16:05:00 +08:00
serverParams . playground = {
2020-09-04 09:51:11 +02:00
endpoint : ` ${ strapi . config . server . url } ${ config . endpoint } ` ,
shareEnabled : config . shareEnabled ,
2018-09-10 16:05:00 +08:00
} ;
2018-04-02 16:31:27 +02:00
}
2018-03-27 17:15:28 +02:00
2018-09-10 16:05:00 +08:00
const server = new ApolloServer ( serverParams ) ;
2020-10-07 15:33:02 +02:00
const uploadMiddleware = graphqlUploadKoa ( ) ;
strapi . app . use ( ( ctx , next ) => {
if ( ctx . path === config . endpoint ) {
return uploadMiddleware ( ctx , next ) ;
}
return next ( ) ;
} ) ;
2018-09-10 16:05:00 +08:00
server . applyMiddleware ( {
app : strapi . app ,
2020-09-04 09:51:11 +02:00
path : config . endpoint ,
2018-09-10 16:05:00 +08:00
} ) ;
2020-11-25 17:22:41 +01:00
strapi . plugins . graphql . destroy = async ( ) => {
await server . stop ( ) ;
} ;
2018-09-10 16:05:00 +08:00
} ,
2018-03-27 17:15:28 +02:00
} ;
} ;
2019-04-09 21:51:28 +02:00
/ * *
* Merges a list of schemas
* @ param { Array < Object > } schemas - The list of schemas to merge
* /
const mergeSchemas = schemas => {
return schemas . reduce ( ( acc , el ) => {
const { definition , query , mutation , type , resolver } = el ;
return _ . merge ( acc , {
definition : ` ${ acc . definition || '' } ${ definition || '' } ` ,
query : ` ${ acc . query || '' } ${ query || '' } ` ,
mutation : ` ${ acc . mutation || '' } ${ mutation || '' } ` ,
type ,
resolver ,
} ) ;
} , { } ) ;
} ;