mrkeyoor.com_
Wed 05 Aug 05:02 UTC
npmWeb Backendupdated 05 Aug 2026

graphql

GraphQL.js is the JavaScript reference implementation of GraphQL, the query language for APIs that Facebook created. It is the low-level engine, not a server: it gives you tools to build a type schema (programmatically or from SDL), then parse, validate, and execute queries against that schema, with resolvers that can return values, promises, or arrays of promises. Almost every GraphQL tool in the JavaScript ecosystem, from Apollo to Yoga to codegen and linters, is built on top of this package, which is why it shows up in node_modules even when you never installed it directly. Version 17 is the first new major since 2021 and requires Node 22+.

Verdict

If you are doing GraphQL in JavaScript you will use this package whether you install it directly or not; it is the reference implementation and it earns that role. Just be honest about whether you need GraphQL at all: for single-client TypeScript apps, tRPC or plain REST is usually less total machinery.

API stability4/5The core parse/validate/execute API has been stable for a decade and v16 stayed latest for about five years before 17.0.0 shipped in June 2026; the deduction is the peer-dependency version coupling that makes major upgrades an ecosystem-wide coordination problem.
Docs3/5The README is a thin getting-started that defers to graphql.org; the site's graphql-js guides cover the basics well, but deep API reference for things like custom scalars and execution internals has long meant reading source and type definitions.
Maintenance4/5Maintained under the GraphQL Foundation with steady activity (pushed 2026-07-27, 20.3k stars) and v17 finally landed in 2026, but the years-long gap between majors and heavy reliance on canary tags show a small maintainer bench for how load-bearing the package is.
Ecosystem5/544.8M weekly downloads and effectively the entire JavaScript GraphQL ecosystem (Apollo, Yoga, Relay tooling, codegen, ESLint plugins) builds directly on it as a peer dependency.

Use it if

  • You are building a GraphQL server, gateway, or tooling in JavaScript or TypeScript and want the reference implementation the spec evolves against
  • You need to parse, validate, print, or transform GraphQL documents programmatically (linters, code generators, schema diffing, mock servers)
  • You want to execute queries against a schema over your own transport instead of adopting a full server framework
  • You maintain a library that accepts GraphQL schemas or documents; depending on the reference implementation is the interoperable choice
Skip it if

Setup reality

npm install graphql is trivially easy: zero dependencies, about 58 KB gzipped, works in Node and browsers. The pain is ecosystem coordination, not installation. Nearly every GraphQL tool declares graphql as a peer dependency with its own accepted version range, so upgrading majors (16 to 17) means waiting until every tool in your stack accepts 17, and a stray duplicate copy in node_modules will throw realm errors at runtime. v17 also raises the floor to Node 22. Budget time for the ecosystem to catch up before you migrate an existing stack, and use your package manager's dedupe/overrides when the tree gets messy.

Patterns

Build a schema from SDL and run a querysdl-schema

import { graphql, buildSchema } from 'graphql'

const schema = buildSchema(`
  type Query {
    hello: String
  }
`)

const rootValue = { hello: () => 'world' }

const result = await graphql({ schema, source: '{ hello }', rootValue })
console.log(result) // { data: { hello: 'world' } }

buildSchema gives you a schema without resolvers wired to types; field logic lives on rootValue, which only works one level deep.

Define a schema programmatically with resolversprogrammatic-schema

import {
  GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInt
} from 'graphql'

const UserType = new GraphQLObjectType({
  name: 'User',
  fields: {
    id: { type: GraphQLInt },
    name: { type: GraphQLString }
  }
})

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      user: {
        type: UserType,
        args: { id: { type: GraphQLInt } },
        resolve: (_src, args) => ({ id: args.id, name: 'ada' })
      }
    }
  })
})

Programmatic schemas attach resolve functions per field, which is what buildSchema cannot do; most servers use this or a schema-building library.

Execute a query with variablesquery-variables

import { graphql } from 'graphql'

const source = `
  query GetUser($id: Int!) {
    user(id: $id) { name }
  }
`

const result = await graphql({
  schema,
  source,
  variableValues: { id: 42 }
})

Variable names in variableValues omit the leading $; type mismatches surface as entries in result.errors, not thrown exceptions.

Handle execution errors properlyerror-handling

const result = await graphql({ schema, source })

if (result.errors) {
  for (const err of result.errors) {
    console.error(err.message, err.path, err.locations)
  }
}
// result.data can be partially non-null even when errors exist

GraphQL returns partial data alongside errors; treating any errors array as total failure throws away successfully resolved fields.

Validate a query without executing itvalidate-document

import { parse, validate } from 'graphql'

const document = parse('{ user(id: 1) { nam } }')
const errors = validate(schema, document)

if (errors.length > 0) {
  console.error(errors.map((e) => e.message))
  // e.g. Cannot query field "nam" on type "User".
}

parse throws GraphQLError on syntax problems, so wrap it in try/catch; validate returns an array and never throws.

Parse and pretty-print a documentparse-print-ast

import { parse, print } from 'graphql'

const ast = parse('query { user(id:1){ name } }')
console.log(print(ast))
// query {
//   user(id: 1) {
//     name
//   }
// }

print normalizes formatting, which makes it useful for diffing and persisting operations in a canonical form.

Use execute() when you parse and validate yourselfexecute-directly

import { parse, validate, execute } from 'graphql'

const document = parse(source)
const validationErrors = validate(schema, document)
if (validationErrors.length > 0) throw new Error('invalid query')

const result = await execute({
  schema,
  document,
  variableValues: { id: 42 },
  contextValue: { db }
})

Servers cache parsed and validated documents and call execute directly; the graphql() helper re-validates on every call.

Define a custom scalar typecustom-scalar

import { GraphQLScalarType, Kind } from 'graphql'

const DateScalar = new GraphQLScalarType({
  name: 'Date',
  serialize: (value) => value.toISOString(),
  parseValue: (value) => new Date(value),
  parseLiteral: (ast) =>
    ast.kind === Kind.STRING ? new Date(ast.value) : null
})

parseValue handles variables, parseLiteral handles inline values in the query text; forgetting one of the two is the classic custom-scalar bug.

Execute synchronously when all resolvers are syncsync-execution

import { graphqlSync, buildSchema } from 'graphql'

const schema = buildSchema('type Query { version: String }')
const result = graphqlSync({
  schema,
  source: '{ version }',
  rootValue: { version: '1.0.0' }
})

graphqlSync throws if any resolver returns a promise; it is handy for build-time tooling and introspection scripts.

Introspect a schema to JSONintrospection

import { graphqlSync, getIntrospectionQuery, buildClientSchema } from 'graphql'

const introspection = graphqlSync({
  schema,
  source: getIntrospectionQuery()
}).data

// round-trip: rebuild a client-side schema from the JSON
const clientSchema = buildClientSchema(introspection)

This JSON is what GraphiQL and codegen tools consume; production servers often disable introspection, so generate it at build time.

Export a schema as SDL textprint-schema-sdl

import { printSchema } from 'graphql'

const sdl = printSchema(schema)
// write it to schema.graphql for codegen, review, and diffing
import { writeFileSync } from 'node:fs'
writeFileSync('schema.graphql', sdl)

Checked-in SDL snapshots make schema changes visible in code review; regenerate on every schema edit or the file lies.

Alternatives

PackageRegistryPick it when
@apollo/servernpmYou want a maintained, spec-compliant GraphQL HTTP server rather than wiring the bare engine yourself
graphql-yoganpmYou want a batteries-included GraphQL server with sane defaults that runs on Node and edge runtimes
@trpc/servernpmBoth ends are TypeScript you control and you want end-to-end types without a query language