thompsonsj / gatbsy-source-payload-cms

MIT License
5 stars 0 forks source link

Research how best to handle schema customization #12

Open thompsonsj opened 1 year ago

thompsonsj commented 1 year ago

References

Plugins that use GraphQL/schemas (other CMS implementations):

thompsonsj commented 1 year ago

Generating schemas from Payload and passing to Gatsby

Terminology

graphql-js types

TODO: Investigate.

From https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization/#gatsby-type-builders:

Note that the createTypes action also accepts graphql-js types directly, but usually either SDL or Type Builders are the better alternatives.

SDL

SDL can be generated from Payload CMS. In the following example, a route is added to server.ts to generate Gatsby schema customizations on demand:

import { printSchema, GraphQLSchema } from 'graphql'
import buildObjectType from 'payload/dist/graphql/schema/buildObjectType'

// Collection
import Testimonials from './collections/Logos/Testimonials'

app.get('/gatsby-schema-types', async (_, res) => {
  const gatsbyNodeType = "PayloadTestimonials"

  const testimonials = buildObjectType({
    payload,
    name: gatsbyNodeType,
    parentName: gatsbyNodeType,
    fields: Testimonials.fields
  })
  const schema = new GraphQLSchema({query: testimonials})

  const report = printSchema(schema)

  res.setHeader('content-type', 'application/json')
  res.send(report)
})

This will print the schema for collection fields (as defined).

schema {
  query: PayloadTestimonials
}

type PayloadTestimonials {
  locales: [PayloadTestimonials_locales!]
  name: String!
  jobTitle: String
}

"""
Additional definitons....
"""

A more complicated example that:

import { Field } from 'payload/types'
import { buildSchema, printSchema, GraphQLSchema } from 'graphql'
import { mergeSchemas } from '@graphql-tools/schema'
import buildObjectType from 'payload/dist/graphql/schema/buildObjectType'

import Testimonials from './collections/Logos/Testimonials'
import ATS from './global/ATS'

app.get('/gatsby-schema-types', async (_, res) => {
  interface IgatsbyNodeType {
    gatsbyNodeType: string
    fields: Field[]
  }

  const gatsbyNodeTypes: IgatsbyNodeType[] =[
    {
      gatsbyNodeType: "PayloadTestimonials",
      fields: Testimonials.fields,
    },
    {
      gatsbyNodeType: "PayloadAts",
      fields: ATS.fields,
    }
  ]

  const schemas = gatsbyNodeTypes.map((node) => {
    const query = buildObjectType({
      payload,
      name: node.gatsbyNodeType,
      parentName: node.gatsbyNodeType,
      fields: Testimonials.fields
    })
    const schema = new GraphQLSchema({query})
    const sdl = printSchema(schema)
      // Payload's JSON scalar conflicts with a built-in/Gatsby scalar
      .replaceAll('JSON', 'PayloadJSON')
      // replace backticks so we can paste the result into a string literal
      .replaceAll('`', "\"")
      // Gatsby won't load in the schema/query definition
      .replace('schema {', '')
      .replace(`  query: ${node.gatsbyNodeType}`, '')
      .replace('}', '')
    return buildSchema(sdl)
  })

  const merged = mergeSchemas({
    schemas
  })

  let report = printSchema(merged)

  gatsbyNodeTypes.forEach(node => {
    // Add Gatsby's Node interface to each type
    report = report.replace(`type ${node.gatsbyNodeType} {`, `type ${node.gatsbyNodeType} implements Node {`)
  })

  res.setHeader('content-type', 'application/json')
  res.send(report)
})

Required changes to output

The generated SDL needs to be modified before use with Gatsby's createTypes action.

TODO: Investigate an automated approach - are there any Gatsby/Payload/graphql classes/functions that can help?

Add implements Node

On each type that corresponds to a Gatsby Node type, implement the Node interface.

type PayloadTestimonials implements Node

See https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization/#the-node-interface.

Remove schema from the top

schema {
  query: PayloadTestimonial
}

If this is not removed, the following error is produced on gatsby develop:

Missing onError handler for invocation 'building-schema', error was 'TypeError: Cannot read properties of undefined (reading 'value')'.
Stacktrace was 'TypeError: Cannot read properties of undefined (reading 'value')

Remove backticks in the generated output

The generated SDL include comments (wrapped in """) that can use backticks - making it impossible to use a string literal. Replace with standard quotes or remove.

Build-in scalar type error for the JSON scalar

Missing onError handler for invocation 'building-schema', error was 'Invariant Violation: The GraphQL type `JSON` is reserved for internal use
by built-in scalar types.'. Stacktrace was 'Invariant Violation: The GraphQL type `JSON` is reserved for internal use by built-in scalar types.