I opened issue #16736 last year, and now I've come back to this project. The solution I apparently found back then (illustrated above) doesn't seem to work now. I wonder if createSchemaCustomization has changed since then, or (more likely) what simple thing I've missed.
I've created a minimal example to illustrate this issue: https://github.com/mbwatson/gatsby-links.
This example has two data types--_people_ and _teams_--sourced from YAML files in the ./data directory. To summarize, the aim is to see the members of a team when a team is queried, and (2) to see the teams to which a person belongs when a person is queried.
People files look like the following:
id: alice-albertson
name: Alice Albertson
bio: >
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
Teams YAML files look like this:
id: team-awesome
name: Team Awesome
description: >
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Temporibus ipsam provident quasi soluta ex fugit, asperiores
itaque voluptas. Cupiditate nisi enim eius rem tempore
debitis eum impedit fugiat nostrum magnam.
members:
- alice-albertson
- carla-cranston
Using mapping in gatsby-config.js I can see members as people nodes when querying teams:
{
allTeamsYaml {
edges {
node {
id
name
members {
id
name
}
}
}
}
}
{
"data": {
"allTeamsYaml": {
"edges": [
{
"node": {
"id": "team-awesome",
"name": "Team Awesome",
"members": [
{
"id": "alice-albertson",
"name": "Alice Albertson"
},
{
"id": "carla-cranston",
"name": "Carla Cranston"
}
]
}
},
{
"node": {
"id": "team-mediocre",
"name": "Team Mediocre",
"members": [
{
"id": "alice-albertson",
"name": "Alice Albertson"
},
{
"id": "bob-bobberson",
"name": "Robert Bobberson"
}
]
}
}
]
}
}
}
However, I can't seem to see the teams when querying a person. My thinking was to add
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type PeopleYaml implements Node {
teams: [TeamsYaml] @link(by: "members.id", from: "id")
}
`
createTypes(typeDefs)
}
to gatsby-node.js, but the results have null for teams.
{
allPeopleYaml {
edges {
node {
id
name
teams {
id
}
}
}
}
}
{
"data": {
"allPeopleYaml": {
"edges": [
{
"node": {
"id": "alice-albertson",
"name": "Alice Albertson",
"teams": null
}
},
{
"node": {
"id": "bob-bobberson",
"name": "Robert Bobberson",
"teams": null
}
},
{
"node": {
"id": "carla-cranston",
"name": "Carla Cranston",
"teams": null
}
}
]
}
}
}
Am I doing something silly here? Any help resolving this would be greatly appreciated!
gatsby-config.js:
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `@gatsbyjs`,
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
},
},
`gatsby-transformer-yaml`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./src/data/`,
},
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// `gatsby-plugin-offline`,
],
mapping: {
"TeamsYaml.members": "PeopleYaml",
},
}
gatsby-node.js:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type PeopleYaml implements Node {
teams: [TeamsYaml] @link(by: "members.id", from: "id")
}
`
createTypes(typeDefs)
}
Thank you for opening this!
This is indeed not your fault, it's merely the fact that the by from the @link directive doesn't support arrays. Because otherwise it would have worked with this:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type TeamsYaml implements Node {
members: [PeopleYaml] @link(by: "id")
}
type PeopleYaml implements Node {
teams: [TeamsYaml] @link(by: "members", from: "id")
}
`
createTypes(typeDefs)
}
(After removing the mapping from gatsby-config.js)
Thus you'll need to use the createResolvers API for the PeopleYaml part:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type TeamsYaml implements Node {
members: [PeopleYaml] @link(by: "id")
}
`
createTypes(typeDefs)
}
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
PeopleYaml: {
teams: {
type: ["TeamsYaml"],
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
members: {
elemMatch: {
id: {
eq: source.id,
}
}
}
}
},
type: "TeamsYaml",
firstOnly: false,
})
}
}
}
}
createResolvers(resolvers)
}
It's doing a query on the TeamsYaml type (and with firstOnly: false effectively an allTeamsYaml) and is filtering by the id of PeopleYaml.
Result:

I opened the issue https://github.com/gatsbyjs/gatsby/issues/25089 to document the need for a docs update on that! We'd be happy to receive a PR from you updating the docs in that regard.
We're marking this issue as answered and closing it for now but please feel free to comment here if you would like to continue this discussion. We also recommend heading over to our communities if you have questions that are not bug reports or feature requests. We hope we managed to help and thank you for using Gatsby!
@LekoArts Thank you so much!
I ran into exactly the same issue, and while @LekoArts' answer above provides the solution, I just wanted to expand on it a little for reference if others run into this. This was working fine for me until I updated my Gastby install, but I was on a pretty older version so I'm not sure when @link stopped working for arrays.
Partially because I'm not using any type inference, and because I've got a lot of models and multiple relationships, I needed to poke around a bit to get the correct implementation. Do bear in mind I'm relatively new to Gatsby so this may well not be very elegant or performant, but it is working for me at the moment, and my build times are not an issue with a few thousand nodes.
So first of all, if you're explicitly declaring all your fields in a model, you would declare a relationship that is going to require a resolver, but not give it an @link. For example, for in a model where an author may have written books as well as blog posts that can have more than one author (held in a custom 'author_id' field added to MarkdownRemark):
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type Book implements Node @dontInfer {
title: String!
alternate_work_id: ID
slug: String!
authors: [Author] @link(by: "alternate_id")
}
type Author implements Node @dontInfer {
name: String!
keyName: String
nameBeforeKey: String
bio: String
slug: String!
role: String!
alternate_id: ID
posts: [MarkdownRemark]
books: [Book]
}
`
createTypes(typeDefs)
}
You then resolve those fields with the custom resolvers:
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
Author: {
books: {
type: ["Book"],
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
authors: {
elemMatch: {
alternate_id: {
eq: source.alternate_id,
},
},
},
},
},
type: "Book",
firstOnly: false,
})
},
},
posts: {
type: ["MarkdownRemark"],
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
authors: {
elemMatch: {
author_id: {
eq: source.author_id,
},
},
},
},
},
type: "MarkdownRemark",
firstOnly: false,
})
},
},
},
}
createResolvers(resolvers)
}
Do note now I'm resolving both fields within that author object. You'll run into problems overwriting yourself if you try and do a new resolver for 'Author' underneath this one for resolving a different field for 'Author'. You can create multiple resolvers for as many types as you want though, you would just add the new type as an object at the same level as 'Author'. For example, if Book also had a custom resolver:
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
Author: { ...resolver_code },
Book: { ...resolver_code },
}
createResolvers(resolvers)
}
I hope this is helpful if others run into the same issue.
related: #25336
Most helpful comment
Thank you for opening this!
This is indeed not your fault, it's merely the fact that the
byfrom the@linkdirective doesn't support arrays. Because otherwise it would have worked with this:(After removing the mapping from gatsby-config.js)
Thus you'll need to use the
createResolversAPI for thePeopleYamlpart:It's doing a query on the
TeamsYamltype (and withfirstOnly: falseeffectively anallTeamsYaml) and is filtering by theidofPeopleYaml.Result:
I opened the issue https://github.com/gatsbyjs/gatsby/issues/25089 to document the need for a docs update on that! We'd be happy to receive a PR from you updating the docs in that regard.
We're marking this issue as answered and closing it for now but please feel free to comment here if you would like to continue this discussion. We also recommend heading over to our communities if you have questions that are not bug reports or feature requests. We hope we managed to help and thank you for using Gatsby!