@miragejs/graphql
Version:
A library for handling GraphQL requests with Mirage JS
81 lines (73 loc) • 3.18 kB
text/typescript
import { GraphQLFieldResolver, GraphQLSchema, graphql } from "graphql";
import { Response } from "miragejs";
import createFieldResolver from "./resolvers/field.js";
import { createModels } from "./orm/models.js";
import { ensureExecutableGraphQLSchema } from "./utils.js";
import type { AnyRegistry } from "miragejs/-types";
import type {
MirageSchema,
ResolverMap,
SourceGraphQLSchema,
} from "./@types/index.js";
import type { RouteHandler } from "miragejs/server";
/**
* Options for creating a GraphQL request handler for Mirage.
*
* 1. `context` - A context object that GraphQL will pass into each resolver. A
* common use case for this is to supply current user information to
* resolvers. By default, whatever context you pass in will be appended with
* a reference to the Mirage schema and the request being handled.
* 2. `resolvers` - A resolver map for cases where the default Mirage resolvers
* aren't sufficient. Such cases include: resolving root-level scalar values,
* sorting records and complex mutations.
* 3. `root` - A root level value that GraphQL will use as the parent object for
* fields at the highest level.
*/
type CreateHandlerOptions = {
context?: { [key: string]: any };
resolvers?: ResolverMap;
root?: any;
};
/**
* A higher-order function that returns a request handler for GraphQL queries.
* It accepts both a GraphQL and Mirage schema along with a hash of options.
*
* The GraphQL schema param may be a string, an AST or an executable
* GraphQL schema. This library ensures the schema is executable in any case.
*
* This also ensures models are added to the Mirage schema for each appropriate
* type from the GraphQL schema. Since the GraphQL schema already defines types
* and relationships, it may be redundant to define Mirage models when using
* GraphQL. You may still define Mirage models, though, if you’d like.
*
* Lastly, it creates a field resolver that GraphQL will use to resolve every
* field from a query. If an optional resolver isn't supplied for a given field,
* this field resolver will be used. It does its best to resolve queries and
* mutations automatically based on the information from the GraphQL schema and
* the records in Mirage’s database.
*/
export function createGraphQLHandler(
graphQLSchema: SourceGraphQLSchema,
mirageSchema: MirageSchema,
options: CreateHandlerOptions = {}
): RouteHandler<AnyRegistry> {
const { context = {}, resolvers, root } = options;
const fieldResolver = createFieldResolver(resolvers);
graphQLSchema = ensureExecutableGraphQLSchema(graphQLSchema);
createModels(graphQLSchema, mirageSchema);
return function graphQLHandler(_, request) {
try {
const { query, variables } = JSON.parse(request.requestBody);
return graphql({
contextValue: { ...context, mirageSchema, request },
fieldResolver: fieldResolver as GraphQLFieldResolver<any, any>,
rootValue: root,
schema: graphQLSchema as GraphQLSchema,
source: query,
variableValues: variables,
});
} catch (ex) {
return new Response(500, {}, { errors: [ex] });
}
};
}