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

nest-graphql-endpoint

v0.7.1

Published

Nest an external GraphQL Endpoint

Downloads

28

Readme

Nest an external GraphQL Endpoint (Like GitHub's v4 API)

How it works

https://www.parabol.co/blog/nest-github-api-in-graphql-schema

Install

yarn add nest-graphql-endpoint

What's it do?

Merges a remote graphql endpoint into your schema. For example, let's say your app has a GitHub integration. Each user has used OAuth2 to allow your app to access GitHub on their behalf. You have stored that token on the User object in your database. Now, you want to get their name from your app & their bio from GitHub. You also want the bio of all their friends, too. This package will let you write the following query:

query {
  user(id: 'abc') {
    name
    github {
      errors {
        message
      }
      query {
        # These values come from GitHub
        viewer {
          bio
        }
      }
    }
    friends {
      github {
        errors {
          message
        }
        query {
          viewer {
            bio
          }
        }
      }
    }
    todos {
      ... on _extGitHubIssue {
        # These values come from GitHub, too
        number
        repository {
          nameWithOwner
        }
      }
    }
  }
}

Example with your own endpoint

import {schema} from './mySchema'
import nestGraphQLEndpoint from 'nest-graphql-endpoint'
import {print} from 'graphql'
import schemaIDL from './remoteSchemaIDL'

return nestGraphQLEndpoint({
  parentSchema: schema,
  parentType: 'User',
  fieldName: 'github',
  resolveEndpointContext: (source) => ({accessToken: source.accessToken}),
  executor: (document, variables, context) => {
    // Example executor fn not for production! See nestGitHubEndpoint for a production-ready executor
    return fetch('https://foo.co', {
      headers: {
        Authorization: `Bearer ${context.accessToken}`,
      },
      body: JSON.stringify({query: print(document), variables}),
    })
  },
  prefix: '_extEndpoint',
  batchKey: 'accessToken',
  schemaIDL,
})

As a convenience, this package includes a helper for GitHub's API. If you'd like to add other endpoints, please make a PR.

Example using GitHub's API

import {schema} from './mySchema'
import nestGitHubEndpoint from 'nest-graphql-endpoint/lib/nestGitHubEndpoint'

return nestGitHubEndpoint({
  parentSchema: schema,
  parentType: 'User',
  fieldName: 'github',
  // Assumes a `githubToken` is on the User object
  // GitHub preview is being phased out, but still requires a custom header to access preview features
  resolveEndpointContext: (source) => ({
    accessToken: source.githubToken,
    headers: {Accept: 'application/vnd.github.bane-preview+json'},
  }),
}).schema

Note: Requires Node v15.0.0 to use the native AbortController

Example field resolution

Say each User has a list of todos and some of those come from Jira & others come from GitHub.

type User {
  id: ID!
  todos: [Todo]
}
interface Todo {
  id: ID!
}
type _extGitHubIssue implements Todo
type JiraIssue implements Todo
// Front-end
const UserQuery = graphql`
  query UserQuery($userId: ID!) {
    user(id: $userId) {
      todos {
        __typename
        ... on JiraIssue {
          descriptionHTML
        }
      ... on  _extGitHubIssue {
        bodyHTML
        number
        respository {
          nameWithOwner
        }
      }
    }
  }
`;
const ShowTodo = (props) => {
  const data = useQuery(UserQuery)
  if (!data) return null
  return data.todos.map((todo) => {
    if todo.__typename === 'JiraIssue') {
      return <JiraTodo todo={todo}/>
    }
    return <GitHubTodo todo={todo}/>
  })
}
// Backend
const {schema, githubRequest} = nestGitHubEndpoint({...})

const todoResolver = (source, args, context, info) => {
  if (source.type === 'github') {
    const ghContext = {accessToken: '123'}
    const {nameWithOwner, issueNumber} = source
    const [repoOwner, repoName] = nameWithOwner.split('/')
    const wrapper = `
          {
            repository(owner: "${repoOwner}", name: "${repoName}") {
              issue(number: ${issueNumber}) {
                ...info
              }
            }
          }`
    const {data, errors} = await githubRequest(wrapper, ghContext, context, info)
    return data // returns
  }
}

How it works

  1. It extends your schema with a type that contains {errors, query, mutation}
  2. Given a remote IDL, it prefixes the __typename so there are no conflicts with your own schema
  3. It batches all requests by the batchKey so only 1 request is made per key. In the above example, this is the accessToken.
  4. For each batched request, it removes the __typename prefix and merges the fragments, variableDefinitions, and variables.
  5. In the event of a name conflict, it will alias fields before the request is fetched.
  6. When the endpoint responds, it will de-alias the response, re-apply the __typename prefix, and filter the errors by path
  7. For field resolvers, it removes any types that don't exist on the endpoint & then sends the request.

Limnitations

  • FragmentDefinitions are assumed equal if they have matching names (Relay compiler guarantees this)

License

MIT