npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@coderich/graphql-shape

v2.3.0

Published

Transform GraphQL response data to desired shape

Downloads

43

Readme

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

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