With Relay
Using pg_grapqhl with Relay.
pg_graphql implements the GraphQL Global Object Identification Specification (Node
interface) and the GraphQL Cursor Connections Specification to be compatible with Relay.
Relay Setup
Pre-requisites
Follow the Relay Installation Guide.
Configuring the Relay Compiler
Modify your relay.config.js
file to reflect the following:
_20module.exports = {_20 // standard relay config options_20 src: './src',_20 language: 'typescript',_20 schema: './data/schema.graphql',_20 exclude: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**'],_20 // pg_graphql specific options_20 schemaConfig: {_20 nodeInterfaceIdField: 'nodeId',_20 nodeInterfaceIdVariableName: 'nodeId',_20 },_20 customScalarTypes: {_20 UUID: 'string',_20 Datetime: 'string',_20 JSON: 'string',_20 BigInt: 'string',_20 BigFloat: 'string',_20 Opaque: 'any',_20 },_20}
schemaConfig
tells the Relay compiler where to find thenodeId
field on thenode
interfacecustomScalarTypes
will improve Relay's type emission
For Relay versions older than v16.2.0, it should be named customScalars
instead.
Configuring your Relay Environment
This example uses Supabase for the GraphQL server, but pg_graphql can be used independently.
_54import {_54 Environment,_54 FetchFunction,_54 Network,_54 RecordSource,_54 Store,_54} from 'relay-runtime'_54_54import supabase, { SUPABASE_ANON_KEY, SUPABASE_URL } from './supabase'_54_54const fetchQuery: FetchFunction = async (operation, variables) => {_54 const {_54 data: { session },_54 } = await supabase.auth.getSession()_54_54 const response = await fetch(`${SUPABASE_URL}/graphql/v1`, {_54 method: 'POST',_54 headers: {_54 'Content-Type': 'application/json',_54 apikey: SUPABASE_ANON_KEY,_54 Authorization: `Bearer ${session?.access_token ?? SUPABASE_ANON_KEY}`,_54 },_54 body: JSON.stringify({_54 query: operation.text,_54 variables,_54 }),_54 })_54_54 return await response.json()_54}_54_54const network = Network.create(fetchQuery)_54const store = new Store(new RecordSource())_54_54const environment = new Environment({_54 network,_54 store,_54 getDataID: (node) => node.nodeId,_54 missingFieldHandlers: [_54 {_54 handle(field, _record, argValues) {_54 if (field.name === 'node' && 'nodeId' in argValues) {_54 // If field is node(nodeId: $nodeId), look up the record by the value of $nodeId_54 return argValues.nodeId_54 }_54_54 return undefined_54 },_54 kind: 'linked',_54 },_54 ],_54})_54_54export default environment
getDataID
is the most important option to add, as it tells Relay how to store data correctly in the cache.missingFieldHandlers
is optional in this example but helps with Rendering Partially Cached Data.
Pagination
Say you are working on a Todo app and want to add pagination. You can use @connection
and @prependNode
to do this.
Fragment passed to usePaginationFragment()
_21fragment TodoList_query on Query_21@argumentDefinitions(_21 cursor: { type: "Cursor" }_21 count: { type: "Int", defaultValue: 20 }_21)_21@refetchable(queryName: "TodoListPaginationQuery") {_21 todosCollection(after: $cursor, first: $count)_21 @connection(key: "TodoList_query_todosCollection") {_21 pageInfo {_21 hasNextPage_21 endCursor_21 }_21 edges {_21 cursor_21 node {_21 nodeId_21 ...TodoItem_todos_21 }_21 }_21 }_21}
Mutation to create a new Todo
_10mutation TodoCreateMutation($input: TodosInsertInput!, $connections: [ID!]!) {_10 insertIntoTodosCollection(objects: [$input]) {_10 affectedCount_10 records @prependNode(connections: $connections, edgeTypeName: "TodosEdge") {_10 ...TodoItem_todos_10 }_10 }_10}
Code to call the mutation
_20import { ConnectionHandler, graphql, useMutation } from 'react-relay'_20_20// inside a React component_20const [todoCreateMutate, isMutationInFlight] =_20 useMutation<TodoCreateMutation>(CreateTodoMutation)_20_20// inside your create todo function_20const connectionID = ConnectionHandler.getConnectionID(_20 'root',_20 'TodoList_query_todosCollection'_20)_20_20todoCreateMutate({_20 variables: {_20 input: {_20 // ...new todo data_20 },_20 connections: [connectionID],_20 },_20})