Usage 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | module.exports = {
// standard relay config options
src: './src',
language: 'typescript',
schema: './data/schema.graphql',
exclude: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**'],
// pg_graphql specific options
schemaConfig: {
nodeInterfaceIdField: 'nodeId',
nodeInterfaceIdVariableName: 'nodeId',
},
customScalarTypes: {
UUID: 'string',
Datetime: 'string',
JSON: 'string',
BigInt: 'string',
BigFloat: 'string',
Opaque: 'any',
},
}
|
schemaConfig
tells the Relay compiler where to find the nodeId
field on the node
interface
customScalarTypes
will improve Relay's type emission
Note
For Relay versions older than v16.2.0, it should be named customScalars
instead.
Configuring your Relay Environment
This example uses Khulnasoft for the GraphQL server, but pg_graphql can be used independently.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 | import {
Environment,
FetchFunction,
Network,
RecordSource,
Store,
} from 'relay-runtime'
import khulnasoft, { KHULNASOFT_ANON_KEY, KHULNASOFT_URL } from './khulnasoft'
const fetchQuery: FetchFunction = async (operation, variables) => {
const {
data: { session },
} = await khulnasoft.auth.getSession()
const response = await fetch(`${KHULNASOFT_URL}/graphql/v1`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
apikey: KHULNASOFT_ANON_KEY,
Authorization: `Bearer ${session?.access_token ?? KHULNASOFT_ANON_KEY}`,
},
body: JSON.stringify({
query: operation.text,
variables,
}),
})
return await response.json()
}
const network = Network.create(fetchQuery)
const store = new Store(new RecordSource())
const environment = new Environment({
network,
store,
getDataID: (node) => node.nodeId,
missingFieldHandlers: [
{
handle(field, _record, argValues) {
if (field.name === 'node' && 'nodeId' in argValues) {
// If field is node(nodeId: $nodeId), look up the record by the value of $nodeId
return argValues.nodeId
}
return undefined
},
kind: 'linked',
},
],
})
export 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.
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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | fragment TodoList_query on Query
@argumentDefinitions(
cursor: { type: "Cursor" }
count: { type: "Int", defaultValue: 20 }
)
@refetchable(queryName: "TodoListPaginationQuery") {
todosCollection(after: $cursor, first: $count)
@connection(key: "TodoList_query_todosCollection") {
pageInfo {
hasNextPage
endCursor
}
edges {
cursor
node {
nodeId
...TodoItem_todos
}
}
}
}
|
Mutation to create a new Todo
| mutation TodoCreateMutation($input: TodosInsertInput!, $connections: [ID!]!) {
insertIntoTodosCollection(objects: [$input]) {
affectedCount
records @prependNode(connections: $connections, edgeTypeName: "TodosEdge") {
...TodoItem_todos
}
}
}
|
Code to call the mutation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | import { ConnectionHandler, graphql, useMutation } from 'react-relay'
// inside a React component
const [todoCreateMutate, isMutationInFlight] =
useMutation<TodoCreateMutation>(CreateTodoMutation)
// inside your create todo function
const connectionID = ConnectionHandler.getConnectionID(
'root',
'TodoList_query_todosCollection'
)
todoCreateMutate({
variables: {
input: {
// ...new todo data
},
connections: [connectionID],
},
})
|