Introducing Socket Firewall: Free, Proactive Protection for Your Software Supply Chain.Learn More
Socket
Book a DemoInstallSign in
Socket

graphql-auto-federate

Package Overview
Dependencies
Maintainers
4
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-auto-federate

Automatically federate a GraphQL service

latest
Source
npmnpm
Version
0.3.8
Version published
Maintainers
4
Created
Source

graphql-auto-federate

Automatically federate a GraphQL service

Quick Start

const fastify = require('fastify')
const mercurius = require('mercurius')
const { buildFederatedService } = require('graphql-auto-federate')

const federated = await buildFederatedService({
  url: `http://original-service:1234/graphql`
})

const federatedService = fastify()
federatedService.register(mercurius, {
  ...federated,
  federationMetadata: true,
  jit: 1
})
await federatedService.listen(3001)

const gateway = fastify()
gateway.register(mercurius, {
  services: [{ name: 'auto', url: `http://localhost:3001/graphql` }],
  jit: 1
})
await gateway.listen(3000)

// query the gateway @ port 3000
// curl -X POST -H 'content-type: application/json' -d '{ "query": "{ hello(greeting: \"ciao\") }" }' localhost:3000/graphql

How it works

Given an existing GraphQL service, graphql-auto-federate reads the schema and builds a new service with federation information that acts as a proxy, forwarding requests to the original service:

( gateway ) --> ( federated "proxy" service ) --> ( original service )

graphql-auto-federate discovers as much information as possible from the original service schema, but additional information is typically required for a working federated service.

This can be achieved by specifying __resolveReference resolvers and directives for entity types (see options).

__resolveReference

The __resolveReference resolver is critical for a working federated service. Implementing __resolveReference for entities in this context (a "proxy" to federate an existing service) is not trivial and strongly depends on the schema and entities provided by the original service.

A special forward function is provided along with the regular resolver arguments to facilitate querying the original service (errors are already managed).

Example

options: {
  resolvers: {
    User: {
      __resolveReference: async (self, args, context, info, forward) => {
        const response = await forward({
          query: `{ getUser (id: ${self.id}) { name, fullName } }`
        })

        return {
          ...response.getUser,
          ...self
        }
      }
    }
  }
}

Note: in some cases, __resolveReference is redundant, for example in Query resolvers, the original service provides all of the required information without needing to call __resolveReference again.

API

buildFederatedService

buildFederatedService ({ url, options }) => { schema, resolvers }

Creates the { schema, resolvers } information to build the federated service.

It performs an introspection query to the original service, then augments the schema to produce a federated schema and resolvers.

options should contain additional information for type and resolvers, that are merged and override those that are discovered.

Example

From the original service schema:

type Query {
  getUser(id: ID!): User
  getUsers: [User]!
}

type Mutation {
  createUser(user: InputUser): User
  updateUser(id: ID!, user: InputUser): User
  deleteUser(id: ID!): ID
}

input InputUser {
  name: String!
}

type User {
  id: ID!
  name: String!
  fullName: String
  friends: [User]
}
const { schema, resolvers } = await buildFederatedService({
  url: `http://original-service:1234/graphql`
})

Generated federated schema is:

extend type Query {
  getUser(id: ID!): User
  getUsers: [User]!
}
extend type Mutation {
  createUser(user: InputUser): User
  updateUser(id: ID!, user: InputUser): User
  deleteUser(id: ID!): ID
}
input InputUser {
  name: String!
}
type User @key(fields: "id") {
  id: ID!
  name: String!
  fullName: String
  friends: [User]
}

Generated federated resolvers are:

{
  Query: {
    getUser: (...) => // forward query
    getUsers: (...) => // forward query
  },
  Mutation: {
    createUser: (...) => // forward query
    updateUser: (...) => // forward query
    deleteUser: (...) => // forward query
  },
  User: {
    __resolveReference: (self) => { console.warn('__resolveReference called', self) }
  }
}

url

the url of the original GraphQL service

options

  • auto (boolean)

auto option discovers the schema from the original service and builds the relative federated schema and resolvers (default: true)

  • type

Inject information to the type definition schema, adding @extend or @directives for entity types. These are merged with (and override) the auto discovered ones if any.

  • @extend (boolean) add "extend" to the type
  • @directives (string) add directives as a string to the type, see federation spec for supported directives

Example

From original service schema:

type Query {
  getUser(id: ID!): User
  getUsers: [User]!
}
type User {
  id: ID!
  name: String!
  fullName: String
}

Using options:

options: {
  auto: false,
  type: {
    Query: {
      '@extend': true
    },
    User: {
      '@directives': '@key(fields: "id") @external'
    }
  }
}

Generated federated schema:

extend type Query {
  getUser(id: ID!): User
  getUsers: [User]!
}
type User @key(fields: "id") @external {
  id: ID!
  name: String!
  fullName: String
}
  • resolvers

Provide resolvers, these are merged with (and override) the auto discovered ones if any.

Supported features

Automatic generation of federated schema supports

  • queries
  • mutations
  • entities
  • scalar types
  • enums
  • unions
  • directives

TODO

  • options.loaders
  • headers in graphqlRequest
  • improve __resolveReference resolution
    • provide fields that need to be resolved (from context.__currentQuery?)
    • (from mercurius gateway) do not query __resolveReference if not necessary
  • 100% test coverage
  • use a model for __resolveReference - { query, variables, transform (jsonata) }
  • more advanced examples in "How it works" section
  • support subscriptions in schema/resolvers
  • comments in federated schema
  • jsdoc and type check
  • expose buildFederatedInfo and document it
  • field aliasing on forwarded queries

Keywords

graphql

FAQs

Package last updated on 29 Jan 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts