Skip to content

Latest commit

 

History

History
152 lines (111 loc) · 5.33 KB

refetching-data.md

File metadata and controls

152 lines (111 loc) · 5.33 KB

Refetching Data

The first core assumption that Relay makes about a GraphQL API is that is provides a mechanism for refetching an object. The following steps will configure your GraphQL API to provide this mechanism.

Contents

Include the Global ID Scalar

The GlobalIdScalar helps to abstract the implementation of global identifiers from your application code by exporting a customised ID scalar that automatically handles transformations at the interface layer of the GraphQL API.

Include the GlobalIdScalar into a Nest module as in the example below.

import { Module } from '@nestjs/common'
import { GlobalIdScalar } from 'nestjs-relay'

@Module({ providers: [GlobalIdScalar] })
export class CommonModule {}

The global id scalar will override the default functionality of the ID scalar that comes out of the box from nestjs/graphql.

Create a Node Type

The NodeType decorator helps to abstract the implementation of node types within your application code, and is a drop-in replacement for the ObjectType decorator from @nestjs/graphql.

The Ship type class must also extend NodeInterface, which will add the id field to the schema.

import { NodeType, NodeInterface } from 'nestjs-relay'

@NodeType()
export class Ship extends NodeInterface {
  @Field()
  name: string;
}

The NodeType decorator accepts the same argument signatures as the ObjectType decorator from @nestjs/graphql. The NodeType decorator also enables Connections to be aware of the name of the type.

At this point, your application's schema will contain the following changes:

"""An object with an ID"""
interface Node {
  """The ID of the object"""
  id: ID!
}

type Ship implements Node {
  id: ID!
  name: String
}

Resolve the id Field

Now that the id field has been added to the Ship type, we need to implement a custom resolver to ensure that a global id is resolved from the value we pass to it.

The GlobalIdFieldResolver function automatically implements the resolver behaviour for us.

import { Resolver } from '@nestjs/graphql'
import { GlobalIdFieldResolver } from 'nestjs-relay'
import { Ship } from './ship.type'

@Resolver()
export class ShipResolver extends GlobalIdFieldResolver(Ship) {}

The default behaviour of the id field resolver will serialize a global id from either a number, string or ResolvedGlobalId.

A second argument can be provided to pass additional options to the id field that you would normally find in the FieldResolver decorator from @nestjs/graphql. This is currently limited to the complexity property, as the Relay specification requires this field to be named 'id' and to be non-nullable.

Resolve the node and nodes Root Fields

Now that each object of the Ship type can be uniquely identified, we can implement the node and nodes root fields. These root fields will allow a Relay client to refetch data by providing an id.

The NodeFieldResolver function automatically registers the node and nodes root fields in the GraphQL schema, allowing our application code to handle how to resolve each type.

import { Resolver } from '@nestjs/graphql'
import { NodeInterface, NodeFieldResolver, ResolvedGlobalId } from 'nestjs-relay'

@Resolver(NodeInterface)
export class NodeResolver extends NodeFieldResolver() {
  resolveNode(resolvedGlobalId: ResolvedGlobalId) {
    return null
  }
}

The resolver should be configured to resolve the NodeInterface; this enables it to return any schema type that implements the node interface.

The NodeFieldResolver class requires that your application code implements the resolveNode method, which should contain the logic for determining which type is being requested, then fetching the data for the specified object of that type.

import { Resolver } from '@nestjs/graphql'
import { NodeInterface, NodeFieldResolver, ResolvedGlobalId } from 'nestjs-relay'
import { ShipService } from './ship.service'

@Resolver(NodeInterface)
export class NodeResolver extends NodeFieldResolver() {
  constructor(private shipService: ShipService) {
    super()
  }

  resolveNode(resolvedGlobalId: ResolvedGlobalId) {
    switch(resolvedGlobalId.type) {
      case: 'Ship':
        return this.shipService.findOneById(resolvedGlobalId.toString())
      default:
        return null
    }
  }
}

The ResolvedGlobalId type has two properties - type and id - as well as the toString and toNumber helper methods so that your application code can remain free of the conversion logic.

const resolvedGlobalId = new ResolvedGlobalId({ type: 'Ship', id: '1' })

console.log(resolvedGlobalId.toString()) // '1'
console.log(resolvedGlobalId.toNumber()) // 1

Conclusion

At this point, your application's GraphQL schema will contain the following changes:

type Query {
  """Fetches an object given its ID"""
  node(
    """The ID of an object"""
    id: ID!
  ): Node

  """Fetches objects given their IDs"""
  nodes(
    """The IDs of objects"""
    ids: [ID!]!
  ): [Node]!
}