Skip to main content
@deploybaseline/graphql is the GraphQL kit the Linear and GitHub emulators run on. Use it to serve a GraphQL API from a custom emulator: it reads requests, runs them against your schema, and gives you Relay connections, global IDs, and error helpers. It uses graphql (graphql-js), so queries are validated against your schema exactly as a real server would.

Serve an endpoint

root(context) returns the resolvers for the root fields of Query and Mutation together, keyed by field name. Fields of other types resolve from the objects you return, as in graphql-js. Build the schema with the buildSchema this package exports: graphql-js refuses a schema built by a different copy of the library.

Requests and responses

  • POST takes a JSON body ({ query, variables, operationName }), a form body with the same fields, or an application/graphql body holding the query.
  • GET takes query, variables (JSON), and operationName as query parameters, and runs queries only. A mutation over GET gets HTTP 405 with Allow: POST.
  • Status. By default a request that ran answers 200, even when resolvers returned errors, and a request that could not run answers 400: an empty query, a syntax or validation error, an unknown operation name, or a malformed body. Pass status to follow the vendor; Linear answers any error with 400.
  • Not emulated. A root field that is in the schema but has no resolver answers with the error Query.<field> is not emulated by baseline (extensions.code is NOT_EMULATED), so a client hits a clear gap instead of a confusing null. This lets you start from the vendor’s full schema and add resolvers as you need them. With strictFields, the same goes for every field: a property your object does not have fails as <Type>.<field> is not emulated by baseline instead of reading as null, which matters when the schema is much larger than what you serve. missingField sits in front of that check: Linear uses it to answer unmodeled fields with their type’s empty value (null, false, 0, an empty list or connection) and to fail the rest as not emulated, because @linear/sdk selects every scalar of a model.
  • Errors thrown in a resolver become GraphQL errors with path and locations. graphqlError(message, extensions) adds extensions, such as { code: 'FORBIDDEN' }.

Helpers

Test coverage

graphqlEndpoint declares every root field in the schema (or the operations you list), and records each root field a test resolves. Provider packages list them in their coverage matrix next to routes, so the coverage gates treat Query.issues like GET /issues:
A root field without a resolver may stay not_started; it answers “not emulated”. A supported one that no test reaches fails the runtime gate. pnpm conformance counts root fields apart from routes.

Where it runs

The kit is plain HTTP, so a custom emulator with GraphQL works in the CLI and in createEmulator.