@coderich/graphql-shape

3.0.0 • Public • Published

GraphQLShape

Build Status

Shape the response of your GraphQL queries, declaratively!


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, from left-to-right, and in natural order
  • 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

name arg description
self JSONPath JSONPath from the current field
parent JSONPath JSONPath from the field's parent
root JSONPath JSONPath from the root object
map Transform(s) Iterate field value(s) and apply transform(s) to each
assign Value Assign any value to the field
rename Key Rename the field key
hoist Keep? Hoist all field attributes to the parent and optionally keep field

Core

name arg description eg. to produce
* null Invoke a core object (no method) String(value)
* "new" Instantiate a core object new Array(value)
* Method Invoke a core object method Date.now(value)

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

User

name arg description
push Value(s) Push value(s); return array
unshift Value(s) Unshift value(s); return array
in Value(s) Boolean: if value in values
nin Value(s) Boolean: if value not in values
eq [v1, r1, v2, r2, ..., v?] Return first r# if value === v#; else v?
ne [v1, r1, v2, r2, ..., v?] Return first r# if value !== v#; else v?
gt [v1, r1, v2, r2, ..., v?] Return first r# if value > v#; else v?
gte [v1, r1, v2, r2, ..., v?] Return first r# if value >= v#; else v?
lt [v1, r1, v2, r2, ..., v?] Return first r# if value < v#; else v?
lte [v1, r1, v2, r2, ..., v?] Return first r# if value <= v#; else v?
not null Negate value
or Value(s) Boolean: if any value.concat(values) is truthy
and Value(s) Boolean: if all value.concat(values) are truthy
add Number(s) Add (sum)
sub Number(s) Subtract
div Number(s) Divide
mul Number(s) Multiply
mod Number(s) Modulus
get Path(s) Lodash.get like
set [Key, Value] Lodash.set like
nvl Value(s) Return first ! === null value from [value, ...values]
uvl Value(s) Return first ! === undefined value from [value, ...values]
default Value(s) Return first ! == null value from [value, ...values]
filter RegExp Filter an array of values that match a given RegExp
pick Key(s) Pick only the key(s) you want from the field/object
pairs null Transform 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

Readme

Keywords

Package Sidebar

Install

npm i @coderich/graphql-shape

Weekly Downloads

6

Version

3.0.0

License

none

Unpacked Size

17.5 kB

Total Files

5

Last publish

Collaborators

  • richard.livolsi