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

@parhelion/apollo-juicer

v1.1.6

Published

reusable/extendable graphql queries/mutations

Downloads

12

Readme

🥭 apollo-juicer 🥭

Create simple reusable GraphQL queries.

Table of Contents

Installation

npm i @parhelion/apollo-juicer

Building

git clone https://github.com/haxius/apollo-juicer.git
yarn install
yarn run build

Note: If you wish to see a non-minified and obfuscated compile use yarn run build-dev

Testing / Linting

<...git clone and yarn install>
yarn run test
yarn run lint

Explanation

Traditionally GraphQL queries and mutations dot a modern application. Sometimes they are organized neatly into folders and other times they can be sporatically placed. When changes happen to the queries from the server-side this can result in multiple locations needing updates on the client-side. apollo-juicer attempts to rectfy this by providing a common-root in which one can derive queries.

Traditional example:

file: graphql/products.js

export const PRODUCTS = gql`
    query products($category: ID) {
        products(category: $category) {
            name
            price
        }
    }
`;

export const PRODUCT_TYPES = gql`
    query productTypes {
        productTypes {
            name
        }
    }
`;

Using Apollo it is reasonable to assume that one might consume either of these queries using the Query component:

<Query query={PRODUCT_TYPES}>...</Query>

or

<Query query={PRODUCTS} variables={{ category: "1234" }}>...</Query>

With traditional Apollo if one wanted a component to consume both queries one would either nest a query (ouch), or more properly combine the queries with a special query:

export const PRODUCTS_WITH_TYPES = gql`
    query productsWithTypes($category: ID) {
        products(category: $category) {
            name
            price
        }
        productTypes {
            name
        }
    }
`;

The problem with this is the duplication of work, and duplication of queries. If a change is made on the API one now has more than one place to fix it. Now one could partially rectify this by exporting strings from their GraphQL Files like such:

file: graphql/products.js

export const PRODUCTS = `
    products(category: $category) {
        name
        price
    }
`;

export const PRODUCT_TYPES = `
    productTypes {
        name
    }
`;

Then when they wish to consume it they could do something like this:

export const PRODUCTS_WITH_TYPES = gql`
    query productsWithTypes ($category ID) {
        ${PRODUCTS}
        ${PRODUCT_TYPES}
    }
`;

But what happens when one or more of those data sets have duplicate arguments?

file: graphql/products.js

export const PRODUCTS = `
    products(category; $category) {
        name
        price
    }
`;

export const PRODUCT_TYPES = `
    productTypes(category: $category) {
        name
    }
`;

One would need a solution that would provide unique naming for the arguments and connect the pieces so the output would look like this:

export const PRODUCTS_WITH_TYPES = gql`
    query productsWithTypes($productCategory: ID, $typeCategory: ID) {
        products(category: $productCategory) {
            name
            price
        }
        productTypes(category: $typeCategory) {
            name
        }
    }
`;

Queue apollo-juicer.

Usage

There are two methods exported by apollo-juicer.

  1. buildQuery(<query>)
  2. combineQueries([<query>, <query>, ...]])

The output of which should be self explanatory.

buildQuery

In order to use these methods, one has to construct their queries (currently) as JavaScript objects.

The PRODUCTS query mentioned above would be exported instead as follows:

file: graphql/products.js

export const PRODUCTS = {
    name: "products",
    alias: "product",
    variables: [{ name: "parent", type: "ID" }],
    results: ["name", "price"]
};

export const PRODUCT_TYPES = {
    name: "productTypes",
    alias: "type",
    variables: [{ name: "parent", type: "ID" }],
    results: [ "name" ]
};

Now it's the users choice when or how they want to convert this into an actual gql query. One could export it wrapped or wrap it on import (preferred) but all that is neccessary to convert it into a usable query is:

import { buildQuery } from "@parhelion/apollo-juicer";
import { PRODUCTS } from "./graphql/products.js";

const PRODUCTS_QUERY = buildQuery(PRODUCTS);

...

inSomeComponentsRenderMethod() {
    return (
        <Query query={PRODUCTS_QUERY} variables={{ parent: "1234" }}>
            ...
        </Query>
    )
}

How does this benefit the developer? Did we not just add another layer to constructing a GraphQL query? Enter combineQueries()

combineQueries

Using the example again from above:

file: graphql/products.js

export const PRODUCTS = `
    products(category; $category) {
        name
        price
    }
`;

export const PRODUCT_TYPES = `
    productTypes(category: $category) {
        name
    }
`;

If one wishes to combine multiple queries together without needing to:

  • worry about duplicate argument names
  • write a third query
import { buildQuery } from "@parhelion/apollo-juicer";
import { PRODUCTS, PRODUCT_TYPES } from "./graphql/products.js";

const PRODUCTS_QUERY = combineQueries([PRODUCTS, PRODUCT_TYPES]);

...

inSomeComponentsRenderMethod() {
    return (
        <Query query={PRODUCTS_QUERY} variables={{ productParent: "1234", typeParent: "5678" }}>
            ...
        </Query>
    )
}

Behind the scenes, the query that is generated would look something like this:

query combined($productCategory: ID, $typeCategory: ID) {
    products(category: $productCategory) {
        name
        price
    }
    productTypes(category: $typeCategory) {
        name
    }
}

Wasn't that much cleaner?

  • apollo-juicer uses the query alias to construct deduped arguments
  • PRODUCT and PRODUCT_TYPES are now re-usable ** That is to say, one could combine them in many different areas of the app in many different ways with other query objects without ever needing to rewrite the query. ** In addition to that, if the GraphQL definition for products or productTypes changes, one only has to update the code in one place.

Advanced

Both buildQuery and combineQueries have an extra object parameter that can be passed to request alternative formatting to the resultset.

===

buildQuery(<query>[, {options}])

===

buildQuery can be passed the following options:

wrapper: boolean = true: When set to false, the query (...args) {} portion of the result is omitted. Just returning the data-set. This is used internally by combineQueries when building the individual datasets but can be used if needed elsewhere.

omitGql: boolean = false: When set to true, the result will be returned as a string instead of a graphql-tag.

===

combineQueries([<query>, <query>, ...][, {options}])

===

combineQueries can be passed the following options:

omitGql: boolean = false: When set to true, the result will be returned as a string instead of a graphql-tag.

ClosingThoughts

Ultimately this is not a perfect solution and could be improved upon in a few key ways.

  1. apollo-juicer SHOULD be modified to parse GraphQL strings: This would allow the consumer to simply pass it a traditional GraphQL string and one would not need to objectize their queries. New users to apollo-juicer could simply import it into their project and use the combineQueries method (buildQuery would no longer be needed) right out of the box.
  2. apollo-graphql could officially support parameterized fragments: This would eliminate the need for this project to exist at all and it could be deprecated.

I, the author, think that this is an imperfect and temporary solution. There are probably other (and perhaps better) solutions out there.

Please do not hesitate to open an issue or open a pull request if you think things could be improved.

Support

Please open an issue for support.

Contributing

Please contribute using Github Flow. Create a branch, add commits, and open a pull request.