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

gql-generator-node

v2.1.16

Published

Generate queries as simple function of schema.

Downloads

3,032

Readme

gql-generator-node

CI Unit tests Lint Coverage semantic-release

Generate queries based on GraphQL schema.

npm install gql-generator-node --save-dev 

ToC

Functionality

Given any schema:

type Query {
  user(id: Int!): User!
}

type User {
  id: Int!
  username: String!
  email: String!
  createdAt: String!
}

this library automatically creates queries like:

query user($id: Int!) {
  user(id: $id){
    id
    username
    email
    createdAt
  }
}

Features

It supports all query types:

  • Query
  • Mutation
  • Subscription

as well as all fields descriptors, including unions, interfaces and fragments.

Last but not least it addresses corner cases - like circular reference.

Usage

The most basic usage is to generate all queries at once by passing schema to generateAll function:

import {generateAll} from 'gql-generator-node';
const {queries, mutations, subscriptions} = generateAll(schema);

console.log(mutations.signup);
/*
mutation signup($username: String!, email: String!, password: String!){
  signup(username: $username, email: $email, password: $password){
    token
    user {
      id
      username
      email
      createdAt
    }
  }
}
*/

Advanced usage

Generate single query

import {generateQuery} from "gql-generator-node";

const query = generateQuery({
    field: schema
        .getQueryType()
        .getFields().user
})

console.log(query);
/*
	Query user($user_context_user_details_region_language: String, $user_details_region_language: String, $id: Int!){
	    user(id: $id){
	        id
	        username
	        email
	        createdAt
	        context{
	            user{
	                id
	                username
	                email
	                createdAt
	                details{
	                    ... on Guest {
	                        region(language: $user_context_user_details_region_language)
	                    }
	                    ... on Member {
	                        address
	                    }
	                }
	            }
	            domain
	        }
	        details{
	            ... on Guest {
	                region(language: $user_details_region_language)
	            }
	            ... on Member {
	                address
	            }
	        }
	    }
	}
*/

Limit query fields

By default query is generated with all nested fields (skipping only circular references), however this behavior can be customised by passing skeleton of object we are interested in. For instance:

const query = generateQuery({
    field: schema
        .getQueryType()
        .getFields().user,
    skeleton: {
        'email':
            true
    }
})

console.log(query);
/*
	Query user($id: Int!){
	    user(id: $id){
	        email
	    }
	}
*/

Custom dedupe

As default top variables names correspond to schema while nested ones can be addressed by the path - so all of them can be addressed independently in a declarative way. Ex:

mutation signup($signup_user_context_user_details_region_language: String, $signup_user_details_region_language: String, $email: String!, $username: String!, $password: String!){
    signup(email: $email, username: $username, password: $password){
        token
        user{
            id
            username
            email
            createdAt
            context{
                user{
                    id
                    username
                    email
                    createdAt
                    details{
                        ... on Guest {
                            region(language: $signup_user_context_user_details_region_language)
                        }
                        ... on Member {
                            address
                        }
                    }
                }
                domain
            }
            details{
                ... on Guest {
                    region(language: $signup_user_details_region_language)
                }
                ... on Member {
                    address
                }
            }
        }
    }
}

Yet some applications might take advantage of custom dedupe functions to for instance to send same argument to all sub fields using same name:

gqlGenerator(schema,depth,({args})=>{
        const o = {};
        (args || []).forEach(arg=>{
            o[arg.name] = arg;
        });
        return o;
    })

=>

mutation signup($language: String, $email: String!, $username: String!, $password: String!){
    signup(email: $email, username: $username, password: $password){
        token
        user{
            id
            username
            email
            createdAt
            context{
                user{
                    id
                    username
                    email
                    createdAt
                    details{
                        ... on Guest {
                            region(language: $language)
                        }
                        ... on Member {
                            address
                        }
                    }
                }
                domain
            }
            details{
                ... on Guest {
                    region(language: $language)
                }
                ... on Member {
                    address
                }
            }
        }
    }
}

Example use case

I personally use it to write graphql endpoints tests.

Assuming GraphQL schema:

type Mutation {
  signup(
    email: String!
    username: String!
    password: String!
  ): UserToken!
}

type UserToken {
  token: String!
  user: User!
}

type User {
  id: Int!
  username: String!
  email: String!
  createdAt: String!
}

Before this tool, one needed to write GraphQL API test like this:

test('signup', async () => {
  const query = `mutation signup($username: String!, email: String!, password: String!){
    signup(username: $username, email: $email, password: $password){
      token
      user {
        id
        username
        email
        createdAt
      }
    }
  }`;

  return graphql(query);
});

As gqlGenerator can generate queries, above test becomes:

const {queries} = generateAll(schema.getMutationType().signup);

const variables = { username: "I", email: "[email protected]", password: '1234' };

test.each(Object.entries(queries))('%s', async ([name,query]) => 
  graphql(query,{variables})
);

It not only greatly simplifies testing which might be now automated and batched but also ensures that you would never miss the field to test. Last but not least there is no code duplication between schema and test so most schema updates does not force tests update.

Notes

  • Variable names are derived from argument names, so variables generated from multiple occurrences of the same argument name must be deduped. By default, subtree arguments are given path prefix (ex. can be found in dedupe description).

Credits

Code has has its origins at modelo/gql-generator, however it greatly diverged from this implementation.

Contribution

Please feel free open the issues! Although the current stage satisfies my application usage, I would be happy to provide help and improvements if there will be a need for it. Also you can gratify it with star, if you find it useful.