Search code examples
graphqlgatsbysanity

How to create custom resolvers for Gatsby page queries?


I have a Gatsby application pulling data from Sanity. This is Sanity's schema for the course.js:

import video from './video'

export default {
    // Computer name
    name: `courses`,
    // Visible title
    title: `Courses`,
    type: `document`,
    fields: [
        {
            name: `title`,
            title: `Course title`,
            type: `string`,
            description: `Name of the course`
        },
        {
            name: `slug`,
            title: `slug`,
            type: `slug`,
            options: {
                source: `title`,
                maxLength: 100,
            }
        },
        {
            name: `price`,
            title: `Price`,
            type: `number`
        },
        {
            name: `thumbnail`,
            title: `Thumbnail`,
            type: `image`,
            options: {
                hotspot: true,
            }
        },
        {
            name: `playlist`,
            title: `Playlist`,
            type: `array`,
            of: [
                {
                    title: `Video`,
                    name: `video`,
                    type: `video`,
                }
            ]
        },
    ]
}

And this is Sanity's schema for video.js:

export default {
    // Computer name
    name: `video`,
    // Visible title
    title: `Video`,
    type: `object`,
    fields: [
        { name: `title`, type: `string`, title: `Video title` },
        { name: `url`, type: `url`, title: `URL` },
        { name: `public`, title: `public`, type: `boolean`, initialValue: false }
    ]
}

This Gatsby page query:

{
  allSanityCourses {
    nodes {
      title
      price
      playlist {
        url
        title
        public
      }
    }
  }
}

Results in:

{
  "data": {
    "allSanityCourses": {
      "nodes": [
        {
          "title": "Master VS Code",
          "price": 149,
          "playlist": [
            {
              "url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
              "title": "Introduction",
              "public": true
            },
            {
              "url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
              "title": "Philosophy",
              "public": false
            },
            {
              "url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
              "title": "Tech and Tools",
              "public": false
            },
            {
              "url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
              "title": "Integration",
              "public": true
            },
            {
              "url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
              "title": "Extensions",
              "public": false
            }
          ]
        }
      ]
    }
  },
  "extensions": {}
}

To prevent the url field from being injected into the React component this Gatsby page query runs on (because these urls are paid for), I need to remove it, if the public field is set to false.

I've tried inserting this into gastby-node.js:

exports.createSchemaCustomization = ({ actions, schema }) => {
    const { createTypes } = actions
    const typeDefs = [
        schema.buildObjectType({
            name: "SanityCourses",
            fields: {
                playlist: {
                    type: "[SanityVideo]",
                    url: {
                        type: "String",
                        resolve: (source) => "nope"
                    },
                },
            },
            interfaces: ["Node"],
        }),
    ]
    createTypes(typeDefs)
}

And:

exports.createResolvers = ({ createResolvers }) => {
    const resolvers = {
        SanityCourses: {
            playlist: {
                type: "[SanityVideo]",
                url: {
                    type: "String",
                    resolve(source, args, context, info) {
                        return "nope"
                    },
                }
            },
        },
    }
    createResolvers(resolvers)
}

But neither seems to work. The url field returns the url as always. The resolvers don't even seem to fire (I've tried putting console.log()'s in them).

Any help on how to remove the url field if the public field is set to false, or general direction to go in would be very appreciated.


Solution

  • Ditch the attempt in createSchemaCustomization since you don't need to customize the schema here (though I believe there is a way to achieve what you want using it, it is not expected that the data in it is sourced from existing nodes, and this undeclared dependency can create caching issues).

    Then update your createResolvers function to something like this:

    exports.createResolvers = ({ createResolvers }) => {
      createResolvers({
        SanityVideo: {
          safeUrl: {
            type: "String",
            resolve: (source, args, context, info) => source.public ? source.url : null
          },
        },
      })
    }
    
    1. I don't believe resolvers can replace schema-originated nodes (fields), hence using safeUrl instead of url
    2. The type you are adding a field to is SanityVideo, and it doesn't matter what the parent node is—this will apply to all instances of SanityVideo in your data