Make required fields non-nullable in GraphQL schema

When you create a required field, the schema should reflect that and make the corresponding GraphQL field non-nullable.

Using TypeScript with all the fields being nullable forces you to extensively narrow-type everything or use optional chaining where you might not want to.

This could be accompanied by a prompt for migrating old records that do not have a value for a newly added required field.

That said, if you are using DatoCMS for pre-rending (where the data is being retrieved at build time), having an empty required field is out of the question. Therefore, enabling non-nullable fields without a migration prompt is very much possible for these use cases.

@s.verna I can see that you’ve voted for this feature request; would you be able to give us any insight into what blockers (if any) exist to implement this, and if possible, a rough level of effort on your end? I’m just wanting to understand if this would be something that could be implemented relatively quickly and easily if there was demand for it.

For some context on why I’d really love to see this implemented, I am planning to use DatoCMS in combination with https://www.graphql-code-generator.com/ to generate an easy to use and type safe “SDK” for querying data from DatoCMS, and with the current behavior, I can foresee myself needing to add a lot of unnecessary conditional logic to satisfy the TypeScript compiler, for cases that simply cannot happen in reality. This will ultimately mean that it’ll take more effort to build and maintain any DatoCMS consumption code, and extra code will need be shipped to end users, both of which ultimately feels like a waste of resources.

It’s also worth noting that to my understanding, this would be a backwards compatible change to all current DatoCMS GraphQL APIs, as making a nullable field non-nullable would make the new type of a subtype of the old type, meaning that code that is already built to handle the nullable case would still work.

The problem is that, even for required fields, the value of a field might be null.

Counterintuitive, I know.

Suppose you have a model Article, create a bunch of article records, and THEN add a new (required) field.

In this case, the value for such field in already existing records will be null despite the required validation. As a consequence, such records will be marked as invalid (in REST API, the is_valid attribute will be false, and the UI will show a little red dot next to the record).

The only way to provide non-null GraphQL types would be to have different GraphQL queries that only return valid records, while the current allArticles queries return both valid and invalid records.

Of course, if we stop returning invalid records on the current queries, we would generate a non-retrocompatible change.

Hope this clarifies the underlying issue :confused:

@s.verna thanks for the detailed explanation, makes sense!

I guess adding allValidX + validX query patterns likely wouldn’t be viable, because that would introduce the potential for naming collisions (for example, if there were models called both X and validX). Also, doing so would double the amount of queries created per model, which doesn’t feel ideal, although I’m not sure how you could get around this within the same endpoint, as GraphQL (likely rightfully so) doesn’t have the capability to have the schema differ based on query inputs.


Another option I can think of would be to have another GraphQL endpoint exposed (similar to how the “preview” one already exists: API Endpoints - DatoCMS), maybe called something like “strict”, where the records exposed are always valid? I could potentially see this working because:

  1. From my experience, I think that for most use cases, an API user would only need to use queries from one endpoint or the other (and if they did need to access both valid and invalid records so that they could tell the difference between them, it likely wouldn’t be that hard to make calls to the separate endpoints).
  2. The query names could remain the same, meaning that someone could switch from the normal endpoint to the “strict” version without changing their code.

Of course, this is omitting any thought of the additional complexity that this could likely introduce to your codebase.

Correct, a new endpoint was also my conclusion. It’s a bit of work, so we need to book some time for that. We don’t have an ETA yet :roll_eyes:

2 Likes

This would be an amazing addition to Dato. GQL + TS + Codegen is an amazing combination that massively boosts developer productivity but sadly cannot be used to its fullest in the current setup. Have you already had a chance to scope this feature?