July summary

By Jonas Bengtsson

Aug 03, 2023

During July we reached a major milestone, as of version 0.4.1 we support all the changes to the schema that we intend to support for now (which is basically everything apart from the type of a field). Specifically this means it's now possible to change:

  • if a type or field should be hidden from the published view (called admin only)
  • renaming and deleting types and fields, and
  • renaming and deleting indexes.

You can try it out now in the Playground. One thing to note is that you can't delete an entity type if there are any entities of that type. And right now there isn't a way to delete entities, but that's coming.

The main reason why it's taken this far to support these changes is that the API for updating the schema is declarative. You describe the schema you want, and the system figures out what changes need to be made. And you only need to specify the changes you want to make. So there wasn't a way to express that type OldName should now be called NewName. If you just specify the new name it would create a new type (and similarly for fields). So this creates a new type and leave the old type as (which is not what we want in this case):

await client.updateSchemaSpecification({
valueType: [{ name: "NewName", fields: [] }],
});

To solve this we've introduced the concept of migration actions. So to rename a type you now do:

await client.updateSchemaSpecification({
migrations: [
{
version: 4, // the next version of the schema specification
actions: [
{ action: "renameType", valueType: "OldName", newName: "NewName" },
],
},
],
});

The migration actions become a permanent part of the schema specification and is used to lazily transform existing content that is stored with an older version of the schema. This keeps schema changes fast.

In order to rename or delete a unique value index (e.g. used for slugs), you instead need to use transient migration actions. These are only used once and then discarded. So to rename a unique value index you do:

await client.updateSchemaSpecification({
version: 5,
transientMigrations: [
{ action: "renameIndex", index: "oldName", newName: "newName" },
],
});

If you use the web interface to make changes to the schema, these are of course implementation details you don't need to worry about. But it's very satisfying to have this in place and has taken quite a lot of effort to get it right.

Another important change is that we now enable foreign key support on each connection to the database when using SQLite. This was an oversight, since I thought enabling it once when creating the database schema was enough (but it's not). However, as far as I know this shouldn't have resulted in any bugs, but data integrity is extremely important of course.

All in all, a great month!