@subsquid/apollo-server-core
Version:
Core engine for Apollo GraphQL server
62 lines (55 loc) • 2.96 kB
text/typescript
import { parse } from 'graphql/language';
import { execute, ExecutionResult } from 'graphql/execution';
import { getIntrospectionQuery, IntrospectionQuery } from 'graphql/utilities';
import stableStringify from 'fast-json-stable-stringify';
import type { GraphQLSchema } from 'graphql/type';
import createSHA from './createSHA';
import type { SchemaHash } from 'apollo-server-types';
/*
* This function returns a not particularly stable schema hash derived from a
* GraphQLSchema object. It works by running the `graphql-js` default
* introspection query against the schema and taking a SHA of a JSON encoding of
* the result. It is dependent on the precise introspection query returned by
* `graphql-js` and some of the details of how that library returns its data, so
* upgrading `graphql-js` can change its value. It was created for use in
* apollo-server-plugin-operation-registry (now @apollo/server-plugin-operation-registry) * but it is no longer used there. It is *not* the same as the hash used in
* schema and usage reporting, which is just a hash of the schema SDL document.
*
* For backwards-compatibility reasons, it is still calculated and passed to all
* plugin hooks, but it is not a good idea to use it for anything.
*/
export function generateSchemaHash(schema: GraphQLSchema): SchemaHash {
const introspectionQuery = getIntrospectionQuery();
const document = parse(introspectionQuery);
const result = execute({
schema,
document,
}) as ExecutionResult<IntrospectionQuery>;
// If the execution of an introspection query results in a then-able, it
// indicates that one or more of its resolvers is behaving in an asynchronous
// manner. This is not the expected behavior of a introspection query
// which does not have any asynchronous resolvers.
if (
result &&
typeof (result as PromiseLike<typeof result>).then === 'function'
) {
throw new Error(
[
'The introspection query is resolving asynchronously; execution of an introspection query is not expected to return a `Promise`.',
'',
'Wrapped type resolvers should maintain the existing execution dynamics of the resolvers they wrap (i.e. async vs sync) or introspection types should be excluded from wrapping by checking them with `graphql/type`s, `isIntrospectionType` predicate function prior to wrapping.',
].join('\n'),
);
}
if (!result || !result.data || !result.data.__schema) {
throw new Error('Unable to generate server introspection document.');
}
const introspectionSchema = result.data.__schema;
// It's important that we perform a deterministic stringification here
// since, depending on changes in the underlying `graphql-js` execution
// layer, varying orders of the properties in the introspection
const stringifiedSchema = stableStringify(introspectionSchema);
return createSHA('sha512')
.update(stringifiedSchema)
.digest('hex') as SchemaHash;
}