A GraphQL server implementation written in NodeJS/Typescript. It uses the standard graphql library to receive GraphQL requests and send back appropriate responses.
npm install --save @dreamit/graphql-serverTypeScript declarations are provided within the project.
- Creates GraphQL responses for (GraphQL) requests
- Can be use with fitting webservers that provide a matching
RequestandResponseobject (e.g. ExpressJS). - Uses out-of-the-box default options to ease use and keep code short
- Provides hot reloading for schema and options
- Provides out-of-the-box metrics for GraphQLServer
- Uses and is compatible to
graphql-jslibrary version 14 and 15.
You can create a new instance of GraphQLServer with the options necessary for your tasks. The handleRequest function of
the GraphQLServer can be integrated with many fitting webservers.
const graphQLServerPort = 3592
const graphQLServerExpress = express()
const customGraphQLServer = new GraphQLServer({schema: someExampleSchema})
graphQLServerExpress.all('/graphql', (req, res) => {
return customGraphQLServer.handleRequest(req, res)
})
graphQLServerExpress.listen({port: graphQLServerPort})
console.info(`Starting GraphQL server on port ${graphQLServerPort}`)GraphQLServer provides default values and behaviour out of the box. It is recommended to at least provide a schema
so the request won't be rejected because of a missing/invalid schema. When using it with a local schema it is
recommended to provide a rootValue to return a fitting value. Examples for these requests can be found in the
integration test in the GraphQLServer.integration.test.ts class in the tests folder.
Introspection can be used to get information about the available schema. While this may be useful in development environments and public APIs you should consider disabling it for production if e.g. your API is only used with a specific matching frontend.
Introspection can be disabled by adding the NoSchemaIntrospectionCustomRule from the graphql-js library to the
validationRules option.
import {NoSchemaIntrospectionCustomRule} from 'graphql'
const graphQLServerPort = 3592
const graphQLServerExpress = express()
const customGraphQLServer = new GraphQLServer({schema: someExampleSchema, validationRules: [NoSchemaIntrospectionCustomRule]})
graphQLServerExpress.all('/graphql', (req, res) => {
return customGraphQLServer.handleRequest(req, res)
})
graphQLServerExpress.listen({port: graphQLServerPort})
console.info(`Starting GraphQL server on port ${graphQLServerPort}`)Hot reload of the GraphQL schema can be used to update the existing schema to a new version without restarting the GraphQL server, webserver or whole application. When setting a new schema it will be used for the next incoming request while the old schema will be used for requests that are being processed at the moment. Hot reloading is especially useful for remote schemas that are processed in another application like a webservice.
The schema can be changed simply by calling setSchema in the GraphQLServer instance. In the example below a second
route is used to trigger a schema update.
const graphQLServerPort = 3592
const graphQLServerExpress = express()
const customGraphQLServer = new GraphQLServer({schema: someExampleSchema})
graphQLServerExpress.all('/graphql', (req, res) => {
return customGraphQLServer.handleRequest(req, res)
})
graphQLServerExpress.all('/updateme', (req, res) => {
const updatedSchema = someMagicHappened()
customGraphQLServer.setSchema(updatedSchema)
return res.status(200).send()
})
graphQLServerExpress.listen({port: graphQLServerPort})
console.info(`Starting GraphQL server on port ${graphQLServerPort}`)The implementation uses prom-client library to provide default NodeJS metrics as well as three custom metrics for the
GraphQL server:
- graphql_server_availability: Availability gauge with status 0 (unavailable) and 1 (available)
- graphql_server_request_throughput: The number of incoming requests
- graphql_server_errors: The number of errors that are encountered while running the GraphQLServer. The counter uses the errorName field as label so errors could be differentiated. Default label for schema validation errors is "SchemaValidationError", "GraphQLError" for graphql errors and the error name if another type of error occurs.
A simple metrics endpoint can be created by using getMetricsContentType and getMetrics functions from
the GraphQLServer instance. In the example below a second route is used to return metrics data.
const graphQLServerPort = 3592
const graphQLServerExpress = express()
const customGraphQLServer = new GraphQLServer({schema: someExampleSchema})
graphQLServerExpress.all('/graphql', (req, res) => {
return customGraphQLServer.handleRequest(req, res)
})
graphQLServerExpress.get('/metrics', async (req, res) => {
return res.contentType(customGraphQLServer.getMetricsContentType()).send(await customGraphQLServer.getMetrics());
})
graphQLServerExpress.listen({port: graphQLServerPort})
console.info(`Starting GraphQL server on port ${graphQLServerPort}`)The GraphQLServer accepts the following options. Note that all options are optional and can be overwritten by
calling the setOptions function of the GraphQLServer instance.
debug: Iftrueadditional log output will be created.
schema: The schema that is used to handle the request and send a response. If undefined theGraphQLServerwill reject responses with a GraphQL error response with status code 500.formatErrorFunction: Function that can be used to format occurring GraphQL errors. Given aGraphQLErrorit should return aGraphQLFormattedError. By defaultformatErrorfromgraphql-jslibrary is called.schemaValidationFunction: Function that is called when a schema is set or updated. Given aGraphQLSchemait can return aReadonlyArray<GraphQLError>or an empty array if no errors occurred/should be returned. By defaultvalidateSchemafromgraphql-jslibrary is called.parseFunction: Function that is called to create aDocumentNodewith the extracted query in the request information. Given asourceandParseOptionsit should return aDocumentNode. By defaultparsefromgraphql-jslibrary is called.validationRules: Validation rules that are used whenvalidateSchemaFunctionis called. Can be used e.g. to check whether the request contains an introspection query that should be rejected.validationTypeInfo: Validation type info that is used whenvalidateSchemaFunctionis called.validationOptions: Validation options containing{ maxErrors?: number }that is used whenvalidateSchemaFunctionis called.removeValidationRecommendations: Iftrueremoves validation recommendations like "users not found. Did you mean user?". For non-production environments it is usually safe to allow recommendations. For production environments when not providing access to third-party users it is considered good practice to remove these recommendations so users can not circumvent disabled introspection request by using recommendations to explore the schema.validateFunction: Validation function that validates the extracted request against the available schema. By defaultvalidatefromgraphql-jslibrary is called.
logger: Logger to be used in the GraphQL server.TextLoggerandJsonLoggerare available in the module. Own Logger can be used by implementingLoggerinterface.requestInformationExtractor: TheRequestInformationExtractorused to extract information from theRequestand return aPromise<GraphQLRequestInfo>. By default, theDefaultRequestInformationExtractoris used that tries to extract the information from the body and URL params of the request. Own Extractor can be used by implementingRequestInformationExtractorinterface.metricsClient: TheMetricsClientused to collect metrics from the GraphQLServer. By default, theDefaultMetricsClientis used that collects default NodeJS and three custom metrics usingprom-clientlibrary. Own MetricsClient can be used by implementingMetricsClientinterface.
readonly shouldUpdateSchemaFunction?: (schema?: GraphQLSchema) => boolean
readonly collectErrorMetricsFunction?: (error: GraphQLError, request?: Request) => void
readonly rootValue?: unknown | undefined
readonly contextValue?: unknown
readonly fieldResolver?: Maybe<GraphQLFieldResolver<unknown, unknown>>
readonly typeResolver?: Maybe<GraphQLTypeResolver<unknown, unknown>>
readonly executeFunction?: (schema: GraphQLSchema, document: DocumentNode, rootValue?: unknown, contextValue?: unknown, variableValues?: Maybe<{ [key: string]: unknown }>, operationName?: Maybe, fieldResolver?: Maybe<GraphQLFieldResolver<unknown, unknown>>, typeResolver?: Maybe<GraphQLTypeResolver<unknown, unknown>>) => PromiseOrValue
readonly extensionFunction?: (request: Request, requestInformation: GraphQLRequestInfo, executionResult: ExecutionResult) => MaybePromise<undefined | { [key: string]: unknown }>