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

graphql-to-type

v1.1.0

Published

(almost) Fully functional GraphQL request parser written completely using TypeScript's typing system.

Downloads

7

Readme

graphql-to-type npm version badge

(almost) Fully functional GraphQL request parser written completely using TypeScript's typing system. At first, I just wanted to see if it's even possible but turned out it can be actually useful in the end.

https://github.com/ThaFog/graphql-to-type/assets/10232371/12515523-5ddd-479e-8562-b2760cfe1a68

Support progress

  • Transforming operation's selection set to object type
  • Transforming operation's variables to object type
  • Extracting operation type and name
  • Fields' aliases
  • Consuming comments
  • Directives (not functional, ignored in transformer)

Not yet supported:

  • Fragments and fragment spreads

    Not yet implemented to be parsable. It's probably possible to have them working though if some conditions are met.

I'm thinking also about implementing generator for TypeMap (more about TypeMap below). It could be used when schema changes only.

Instalation

$ npm i graphql-to-type -D

How to use

Usage is simple. First of all, just write GraphQL request in some variable and pass inferred type to either GraphQlOperation or GraphQlResponse type. Be sure variable / property is const as inferred type should be exact string.

Note For now type consumes only one and first operation from provided string. Every other operation will be just ignored. Also as of now fragments are not yet recognizable and will cause parser error.

const query = `
    query {
        company {
            ceo
            employees
            infoLinks {
                elon_twitter
            }
        }
    }
`;

type queryResponse = GraphQlResponse<typeof query, TypeMap>;
/*
    {
        data?: {
            company: {
                ceo: string;
                employees: number;
                infoLinks: {
                    elon_twitter: string;
                };
            };
        };
        errors?: GraphQlError[];
        extendsions? Record<string, any>;
    }
*/

Second thing is providing TypeMap object, as it's seen above. This thing should include every query, mutation, scalar and other types to properly map properties to their associated types. It's safe to write it once and store in some separate file. TypeMap consists of types property and optional query, mutation and subscription properties. types should have every GraphQL type mapped to it's TypeScript type. Every operation property should have assigned types to all existing operations.

Having SpaceX API as an example, minimal effort TypeMap to have query above properly mapped should look like this one:

// Based on SpaceX GraphQL schema

export interface Info {
    ceo: string;
    employees: number;
    infoLinks: InfoLinks;
}

export interface InfoLinks {
    elon_twitter: string;
    flickr: string;
    twitter: string;
    website: string;
}

export type TypeMap = {
    query: {
        company: Info;
    };
    types: {
        Info: Info;
        InfoLinks: InfoLinks;
    };
};

In case parser won't be able to find associated type, it assigns unknown instead.

If you just want to map selection set without response object, you can use GraphQlOperation type instead:

const query = `
    query {
        company {
            ceo
            employees
            infoLinks {
                elon_twitter
            }
        }
    }
`;

type queryOperation = GraphQlOperation<typeof query, TypeMap>;
/*
    {
        company: {
            ceo: string;
            employees: number;
            infoLinks: {
                elon_twitter: string;
            };
        };
    };
    }
*/

Typing variables

Extracting variables works in the same way as it is with selection set with a difference this time the type returns object with typed variables:

const query = `
    query ($find: CoresFind, $limit: Int, $offset: Int, $order: String, $sort: String) {
        cores(find: $find, limit: $limit, offset: $offset, order: $order, sort: $sort) {
            block
            missions {
                name
                flight
            }
            original_launch
        }
    }
`;

type queryVariables = GraphQlVariables<typeof query, TypeMap>;
/*
    {
        find: CoreFind;
        limit: number;
        offset: number;
        order: string;
        sort: string;
    }
*/

where TypeMap with minimal effort looks like this:

// Based on SpaceX GraphQL schema

export interface CoresFind {
    asds_attempts: number;
    asds_landings: number;
    block: number;
    id: string;
    missions: string;
    original_launch: Date;
    reuse_count: number;
    rtls_attempts: number;
    rtls_landings: number;
    status: string;
    water_landing: boolean;
}

export type TypeMap = {
    types: {
        String: string;
        Int: number;
        Float: number;
        Boolean: boolean;
        CoresFind: CoresFind;
    };
};

Types Aliases

To simplify life, you can create alias for these types so you won't need to provide TypeMap everytime:

import type { GraphQlResponse, GraphQlOperation, GraphQlVariables } from 'graphql-to-type';
import type { TypeMap } from './type-map';

export type GqlResponse<GraphQl extends string> = GraphQlResponse<GraphQl, TypeMap>;
export type GqlOperation<GraphQl extends string> = GraphQlOperation<GraphQl, TypeMap>;
export type GqlVariables<GraphQl extends string> = GraphQlVariables<GraphQl, TypeMap>;

Available Types

GraphQlOperation

Extracts selection set from operation and transforms it into record type

type GraphQlOperation<GraphQl extends string, Types extends TypeMap>

GraphQlVariables

Extracts variables from operation and transforms them into record type

type GraphQlVariables<GraphQl extends string, Types extends TypeMap>

GraphQlOperationName

Extracts name of the operation. Returns undefined if operation has no name set

type GraphQlOperationName<GraphQl extends string>

GraphQlOperationType

Extracts type of the operation. Returns query if no operation type is provided based on GraphQL spec.

type GraphQlOperationType<GraphQl extends string>

GraphQlResponse

Extracts selection set from operation, transforms it into record type and encloses it in graphQl-compatible response type

type GraphQlResponse<GraphQl extends string, Types extends TypeMap>

GraphQlError

Describes potential error returned by GraphQL

export type GraphQlError = {
    readonly message: string;
    readonly locations: ReadonlyArray<SourceLocation>;
    readonly path?: ReadonlyArray<string | number>;
    readonly extensions?: Readonly<Record<string, any>>;
}