graphql-fields
Turns a GraphQL resolver's info argument into a flat map of every requested field, honoring @include/@skip directives.
Repository Health
Technical Analysis
graphql-fields solves a narrow but common problem in GraphQL servers that proxy to REST or other field-selective backends: knowing exactly which fields a client asked for, at any nesting depth, without walking the query AST by hand. Given the info object passed to any resolver, it flattens all fragments (both inline and named spreads) and merges duplicate field selections into a single nested object, so a resolver can inspect graphqlFields(info) and see a plain JS object mirroring the requested selection set.
It correctly respects @include and @skip directives, excluding fields whose condition evaluates to false, and can optionally attach parsed __arguments to each field or exclude specific field names (like __typename) from the output. The typical use case is building a REST query string or database projection from only the fields a GraphQL client actually asked for, avoiding over-fetching from an underlying API.
What You Get
- A single
graphqlFields(info, obj, opts)function that returns a flat, nested object of requested field names - Automatic flattening of inline fragments and named fragment spreads into the parent selection
- Correct handling of
@include(if:)and@skip(if:)directives, including variable-backed conditions - Optional
processArgumentsmode that attaches a__argumentsarray (with kind/value) to any field with arguments - An
excludedFieldsoption to drop noise fields like__typenamefrom the resulting map - Zero runtime dependencies — a single 129-line module with no external packages required at runtime
Common Use Cases
- Translating a GraphQL query’s field selection into a REST API query string (e.g.
?fields=profile,id) so the underlying service returns only the requested data - Building a database projection (e.g. a Mongo
.select()or SQL column list) that matches exactly what the client’s query asked for, avoiding over-fetching - Detecting at resolve time whether an expensive nested field (like a joined relation) was actually requested before doing the work to compute it
- Passing a normalized field-selection object down to a downstream microservice call so it can trim its own response payload
Under The Hood
Architecture
graphql-fields is a single 129-line CommonJS module (index.js) exporting one function, graphqlFields(info, obj, opts). Internally it recurses through flattenAST, which walks each AST node’s selectionSet.selections (via the getSelections helper), resolves named fragment spreads back to their definitions using info.fragments (via getAST/isFragment), and merges the results into a shared accumulator object. Directive handling (getDirectiveResults, getDirectiveValue) and argument parsing (getArguments, getArgumentValue) are implemented as separate pure-ish helper functions layered on top of the same traversal. The one architectural wrinkle is a module-level mutable options object used to thread processArguments/excludedFields through the recursive calls instead of passing them as parameters — functional but not reentrant-safe if the module were ever called concurrently with different options in the same tick, which is a real (if narrow) risk in a single shared Node process.
Tech Stack
The package has zero runtime dependencies — it consumes only the GraphQLResolveInfo shape from graphql-js without importing the graphql package itself. Development tooling is Babel-based: @babel/core, @babel/preset-env, and @babel/cli compile index.js to build/index.js (the published main entry) targeting “last 2 versions” of browsers plus the current Node. Tests run via Mocha with Node’s built-in assert, and graphql (^15.0.0) is a dev-only dependency used to construct real schemas in tests. CI is a .travis.yml matrix across Node 10/12/14 — dated infrastructure reflecting the project’s 2016-2023 active window.
Code Quality
A single 690-line test file (test/index_spec.js) exercises fragment flattening, duplicate-field merging, and directive/argument handling against realistic nested GraphQLObjectType schemas — coverage is thorough for the library’s narrow surface area, using Mocha’s describe/it structure and plain assert calls rather than a heavier assertion library. There is no TypeScript and no type definitions shipped, no ESLint or Prettier configuration in the repo, and no static analysis beyond what Babel’s transpile step catches. Error handling is minimal — the code assumes well-formed GraphQL AST input and does not defensively guard against malformed info objects.
What Makes It Unique
The library targets one specific, recurring pain point — knowing which fields a GraphQL query selected so a REST-backed or field-selective resolver can avoid over-fetching — and solves it with a small, self-contained AST walk rather than a general-purpose GraphQL execution or introspection layer. Its own README is unusually candid about its limits: it carries a deprecation notice pointing at graphql-parse-resolve-info for use cases this library doesn’t cover, particularly around fragment edge cases and interface/union types. That honesty, combined with its zero-dependency footprint, made it a popular lightweight choice for years, but the maintainer’s own guidance is that a more complete alternative should be preferred for anything beyond straightforward field-flattening today.
Used by 2 apps in this directory
Omnivore
Knowledge Management · Bookmarks Archiving · Note Taking
Self-hosted read-it-later platform with highlights, newsletters, PDFs, and seamless Obsidian and Logseq integration.
twenty
CRM
The open-source CRM you build, ship, and version like the rest of your stack — with customizable objects, AI agents, and a TypeScript SDK.