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-field-subscriptions

v1.0.12

Published

This library allows GraphQL.js servers to treat subscriptions much like queries, in that one can make successive subscriptions through the schema's graph, much like multiple query resolves can be made in succession.

Downloads

5

Readme

graphql-field-subscriptions

This library allows GraphQL.js servers to treat subscriptions much like queries, in that one can make successive subscriptions through the schema's graph, much like multiple query resolves can be made in succession.

Installation

For npm users,

$ npm install graphql graphql-field-subscriptions

For yarn users,

$ yarn add graphql graphql-field-subscriptions

This library supports TypeScript by default, as God intended

The problem (in detail)

Given a resolver map like so,

const schema = gql`
    type Query {
        hello: Hello!
    }

    type Hello {
        world: String!
    }
`

const resolverMap = {
    Query: {
        hello: () => ({}),
    },
    Hello: {
        world: () => "Hello World!",
    },
}

The nature of GraphQL allows us to make a query like so,

{
    hello {
        world
    }
}

And recieve the result,

{
    "data": {
        "hello": {
            "world": "Hello World!"
        }
    }
}

However, the same is not true for subscriptions. If we now have a schema and resolver map like so,

const schema = gql`
    type Subscription {
        hello: Hello!
    }

    type Hello {
        world: String!
    }
`

const resolverMap = {
    Subscription: {
        hello: {
            subscribe: () => toAsyncIterator({ hello: {} }),
        },
    },
    Hello: {
        world: {
            subscribe: () => toAsyncIterator({ world: "Hello World!" }),
        },
    },
}

const toAsyncIterator = x =>
    async function* () {
        yield x
    }

And query the server accordingly,

subscription {
    hello {
        world
    }
}

Then we recieve an error, like so,

{
    "error": {
        "name": "FormatedError",
        "message": "Unknown error",
        "originalError": "Cannot return null value for non-null type."
    }
}

Which is odd, as we haven't returned null anywhere. On further inspection, if we make a small change:

const resolverMap = {
    ...
    Hello: {
        world: {
            resolve: () => 'Hello World!', // new line here
            subscribe: () => toAsyncIterator({ world: 'Hello World!' })
        }
    }
}

Then we get the return value as desired. However, what if we wanted the value of hello.world to change over time? GraphQL.js currently does not allow for this, only allowing subscriptions at the top level of the Subscription query. This certainly isn't in the spirit of GraphQL.

The solution

This library supplies a single function, patchFieldSubscriptions, which allows for this functionality. If we do the following,

import { patchFieldSubscriptions } from 'graphql-field-subscriptions'

...

const resolverMap = patchFieldSubscriptions({
    Subscription: {
        hello: {
            subscribe: () => toAsyncIterator({ hello: {} })
        }
    },
    Hello: {
        world: {
            resolve: () => 'A different string, to show that this works',
            subscribe: () => toAsyncIterator({ world: 'Hello World!' })
        }
    }
})

...

We now get the following result,

{
    "data": {
        "hello": {
            "world": "Hello World!"
        }
    }
}

Not only this, but the value of world, as well as any values at a further depth, can mutate over time.

...

const resolverMap = patchFieldSubscriptions({
    ...
    Hello: {
        world: {
            subscribe: () => (async function* () {
                while (true) {
                    yield `Here's a cool number: ${Math.random()}`
                    await wait(5000) // this function hangs the thread for 5 seconds
                }
            })
        }
    }
})

...
{
    "data": {
        "hello": {
            "world": "Here's a cool number: 0.8345525102611744"
        }
    }
}
...

{
    "data": {
        "hello": {
            "world": "Here's a cool number: 0.6994837333822601"
        }
    }
}
...

{
    "data": {
        "hello": {
            "world": "Here's a cool number: 0.22817198786140014"
        }
    }
}