sanity-io / gatsby-source-sanity

Gatsby source plugin for building websites using Sanity.io as a backend.
https://www.sanity.io/
MIT License
197 stars 61 forks source link

Root object schema does not implement the Gatsby Node interface #19

Open sondrele opened 5 years ago

sondrele commented 5 years ago

Hi.

I'm chasing down an error that's being thrown by Gatsby during the build process. Apologies in advance in case some of my assumptions below are wrong, but here goes:

info [sanity] Fetching remote GraphQL schema
info [sanity] Transforming to Gatsby-compatible GraphQL SDL
info [sanity] Stitching GraphQL schemas from SDL
success onPreBootstrap — 0.236 s
info [sanity] Fetching export stream for dataset
info [sanity] Watch mode enabled, starting a listener
info [sanity] Done exporting!
success source and transform nodes — 0.664 s
warning Type `SanityArticle` declared in `createTypes` looks like a node, but doesn't implement a `Node` interface. It's likely that you should add the `Node` interface to your type def:

`type SanityArticle implements Node { ... }`

If you know that you don't want it to be a node (which would mean no root queries to retrieve it), you can explicitly disable inference for it:

`type SanityArticle @dontInfer { ... }`
error Building schema failed
error Command failed with exit code 1.

It seams like an error gets thrown because I have defined an Article model of type object (instead of document), but I have included this model as a "root schema" in the sanity schema configuration:

// schemas/schema.js
export default createSchema({
  name: "default",
  types: schemaTypes.concat([
    article: {
      type: "object",
      name: "article",
      title: "Article",
      fields: [
        {
          type: "string",
          name: "title",
          title: "Title"
        },
        {
          type: "string",
          name: "content",
          title: "Content"
        }
      ]
    },
    page: {
      type: "document",
      name: "page",
      title: "Page",
      fields: [
          {
          type: "string",
          name: "title",
          title: "Title"
        },
        {
          type: "article",
          name: "article",
          title: "Article"
        }
      ]
    }
  ])
})

While debugging, I'm seeing that the schema SDL that gets generated here becomes something something like this:

type SanityArticle {
  _key: String
  _type: String
  title: String
  content: String
}

type SanityPage implements SanityDocument & Node {
  _id: String
  _type: String
  _createdAt: Date
  _updatedAt: Date
  _rev: String
  _key: String
  title: String
  article: SanityArticle
}

"""
A Sanity document
"""
interface SanityDocument {
  _id: String
  _type: String
  _createdAt: Date
  _updatedAt: Date
  _rev: String
}

Which is why we see the error above. From here, we can test the two different suggestions from the error message:

Implement the Node interface:

I attempted to remove this check, this resulted in a successful build/develop, and I could query the data like this:

query {
  allSanityArticle {
    ...
  }  
}

However, this naive approach is not a solution, since any object type would then be available in the generated SDL (the check is obviously there for a reason).

Disable inference with @dontInfer:

This also results in a successful develop/build, but I will not be able to execute a query for allSanityArticle.

What's the right solution to this problem?

What I would prefer is to extend the SanityArticle type with implements Node (and any other "root object types") so that I can query these in my Gatsby application (basically, treat "root object types" similar to "document types").

Note that, in this case, it is possible to remodel the application data (i.e. making the Article a "document", but that's still not a solution to this problem).

Help would be much appreciated!

rexxars commented 5 years ago

Thanks for the thorough report - this is an interesting problem.

I'm curious why you chose to have the article type as an object and not a document? Are you saying that you have documents of type article, or are they always inline objects?

sondrele commented 5 years ago

I'm still very new to Sanity, so I'm not familiar with the different terms, and I might also be modeling my application "incorrectly".

The reasoning for making the article an object instead of a document was for it to always be embedded inline in other documents, instead of it being a reference (to make for a better UX while editing different kind of content models). Still, in some cases it would still be nice to be able to get all articles independent of which documents they are embedded into.

Intuitively, I've thought that an object type defined in the top level scope would be its own collection/node type, and thus also be directly available in a query (in Gatsby, I still haven't tested/looked into GROQ). However, I'm realizing that this assumption might not be correct? Could it somehow be possible to configure this functionality, or is would the cleanest solution to this problem just be to make the "article" a document type instead?

rexxars commented 5 years ago

Making it a document type still allows it to be embedded as an online object, so that may well be a good solution, but I'm not sure I see the whole picture. We still have to address this in the plugin, so I'm going to give it some more thought.

sondrele commented 5 years ago

@rexxars Yeah, I realized that it could be a document right after my previous post, so I went back and changed it to an embedded article of type "document". This works, but embedded object can still not be queried directly - I'm assuming this is due to how the objects are persisted and queried, so I guess it's expected.

Now, I've also been reading through your documentation, and I can see that Create referenced documents in-place is on Sanity's roadmap. The whole reason for embedding the articles in the first place was better editing UX for the writers, but it seams like this feature would solve part of the problem I'm having here, so maybe I should just do:

  1. Let "article" be a document type
  2. Reference "article" in my other models instead of embedding it
  3. Wait for the feature to be implemented :)