2.3.2 • Published 9 months ago

@coderich/graphql-shape v2.3.2

Weekly downloads
-
License
-
Repository
-
Last release
9 months ago

GraphQLShape

Build Status

Shape the response of your GraphQL queries, declaratively!

This project explores the concept of Query & Transformation Collocation in GraphQL.


Usage

  1. Annotate a query with transformation rules
  2. Parse the query AST/String pre-request
  3. Transform the result post-response
const { parse } = require('@coderich/graphql-shape');

const { query, transform } = parse(annotatedQuery, [options]);
const data = await graphqlClient.request(query, args); // Your own client
const shaped = transform(data);

Annotations (directives)

Annotations can be defined on any field that requires transformation. By default, the directive name is shape and may be configured via options.name when calling parse() annotation | description | .parse() --- | --- | --- @shape | Transform an existing field in the GraphQL Schema | The annotation is removed from the field @_shape | Define/Transform a non-existing field in the GraphQL Schema | The field is removed from the query

Transformations (annotation arguments)

Transformations are performed via annotation arguments where each key:value pair maps to a transformation name:args function call:

  • Transformations are evaluated depth-first (inside-out, bottom-up) and from left-to-right
  • Each transformation assigns it's return value to the annotated field (mutating it)
  • Each transformation receives the current field value as it's first argument

Transformations (by example)

query {
  books @shape(self: "edges[*].node") {
    edges {
      node {
        isbn
        title
        author @shape(self: "name") { name }
        published: publishDate @shape(Date: "new", toISOString: null)

        # Must specify "parent" because self/field/compound is made up (removed from query)
        compound @_shape(parent: "$[isbn,title]", map: [{ toLowerCase: null }, { replace: [" ", "-"] }, { join: ":" }])

        # Hoist all attributes and remove "detail"
        detail @shape(hoist: false) {
          summary
          rating
        }
      }
    }
  }
}
{
  "books": [
    {
      "isbn": "0-061-96436-0",
      "title": "Moby Dick",
      "author": "Herman Melville",
      "published": "1851-10-18T04:56:02.000Z",
      "compound": "0-061-96436-0:moby-dick",
      "summary": "A legendary tale...",
      "rating": "4.90"
    },
    "...",
  ]
}

API

Each transformation falls into 1 of the following lookup tables (referenced in order of preference):

Lib

nameargdescription
selfJSONPathJSONPath from the current field
parentJSONPathJSONPath from the field's parent
rootJSONPathJSONPath from the root object
mapTransform(s)Iterate field value(s) and apply transform(s) to each
assignValueAssign any value to the field
renameKeyRename the field key
hoistKeep?Hoist all field attributes to the parent and optionally keep field

Core

nameargdescriptioneg. to produce
*nullInvoke a core object (no method)String(value)
*"new"Instantiate a core objectnew Array(value)
*MethodInvoke a core object methodDate.now(value)

Where * is one of [Object, Array, Number, String, Boolean, Symbol, Date, RegExp, Set, Map, WeakMap, WeakSet, Buffer, Math, JSON, Intl]

User

nameargdescription
pushValue(s)Push value(s); return array
unshiftValue(s)Unshift value(s); return array
inValue(s)Boolean: if value in values
ninValue(s)Boolean: if value not in values
eqv1, r1, v2, r2, ..., v?Return first r# if value === v#; else v?
nev1, r1, v2, r2, ..., v?Return first r# if value !== v#; else v?
gtv1, r1, v2, r2, ..., v?Return first r# if value > v#; else v?
gtev1, r1, v2, r2, ..., v?Return first r# if value >= v#; else v?
ltv1, r1, v2, r2, ..., v?Return first r# if value < v#; else v?
ltev1, r1, v2, r2, ..., v?Return first r# if value <= v#; else v?
notnullNegate value
orValue(s)Boolean: if any value.concat(values) is truthy
andValue(s)Boolean: if all value.concat(values) are truthy
addNumber(s)Add (sum)
subNumber(s)Subtract
divNumber(s)Divide
mulNumber(s)Multiply
modNumber(s)Modulus
getPath(s)Lodash.get like
setKey, ValueLodash.set like
nvlValue(s)Return first ! === null value from value, ...values
uvlValue(s)Return first ! === undefined value from value, ...values
defaultValue(s)Return first ! == null value from value, ...values
filterRegExpFilter an array of values that match a given RegExp
pickKey(s)Pick only the key(s) you want from the field/object
pairsnullTransform flat array to 2D elements of 2 (pair) length
flatten*Flat.flatten like
unflatten*Flat.unflatten like

Value

Lastly, invoke value.key(...args) if function; otherwise return value (noop).

Extension

You may define (or redefine) a user transformation via:

GraphQLShape.define(name, function); // or
GraphQLShape.define(Map); // { name: function, name: function, ... }

Function signature: (value, ...args) => newValue

2.3.2

9 months ago

2.3.1

9 months ago

2.3.0

1 year ago

2.2.7

1 year ago

2.2.5

1 year ago

2.2.6

1 year ago

2.2.3

1 year ago

2.2.2

1 year ago

2.2.4

1 year ago

2.2.1

1 year ago

2.2.0

1 year ago

2.1.0

1 year ago

2.0.0

1 year ago

1.1.0

1 year ago

1.0.0

1 year ago