Schemas are here!

This release has some big changes to Convex that we’re really excited to show to you all. Two of these are breaking changes that will require you to make minor updates to your app, so read carefully:

  • Schema Support
  • Breaking: Generated Code Changes
  • Breaking: db.update renamed to db.patch
  • TypeScript Type Checking
  • Extra Goodies

As always, let us know what you think of this in the Convex Slack Community!

Schema Support

The biggest feature in this update is schemas. Convex now supports defining the schema of your project in a schema.ts file. The schema file describes the tables in your Convex app and the type of documents stored in each table. It might look something like:

import { defineSchema, defineTable, s } from "convex-dev/schema";

export default defineSchema({
  messages: defineTable({
    body: s.string(),
    time: s.number(),
    user: s.id(),
  }),
  users: defineTable({
    name: s.string(),
  }),
});

Once you define a schema, if you rerun npx convex codegen, Convex will use that schema to generate two new generated files: dataModel.ts and server.ts.

The Document type from dataModel.ts provides document types for all of your tables. You can use these both when writing Convex functions and in your React components:

import { Document } from "../convex/_generated/dataModel";

function MessageView(props: { message: Document<"messages"> }) {
  ...
}

The query and mutation functions in server.ts have the same API as before but now provide a db with more precise types. Functions like db.insert(table, document) now understand your schema. Additionally database queries will now return the correct document type (not any).

A couple of notes about schemas:

  • This is currently a “types only” feature. Adding a schema will give you precise, code generated types, but won't completely prevent you from writing bad data into your tables. Convex will support schema enforcement at runtime in the future.
  • There are still some APIs like db.get(id) that still use loose types like any even if you define a schema. We’ll be fixing this soon too!

Lastly, schemas are optional! We encourage you to start your project schema-less while you’re prototyping and add a schema once you've solidified your plan.

To learn more about schemas, see the documentation at https://docs.convex.dev/using/schemas!

Breaking: Generated Code Changes

We’ve made a couple of changes to our generated code:

  1. The location of our generated React hooks has moved from convex/_generated.ts to convex/_generated/react.ts.
  2. The query and mutation wrappers have moved from our npm package at convex-dev/server into generated code at convex/_generated/server.ts.
  3. There is a new generated file: convex/_generated/dataModel.ts.

Rationale

We’ve added the new generated server.ts and dataModel.ts to give you schema-specific type safety. You can read more about that in “Schema Support” above. We also generate server.ts and dataModel.ts even before you define a schema so that you won’t need to change your imports when you add one.

We’ve moved all of the generated code into the _generated directory to keep it organized now that there are multiple files.

Migration

  1. Run npx convex codegen. This will delete the old code generated file and create the new ones.
  2. Update all of your import paths from convex/_generated to convex/_generated/react to import the React hooks from the new location.
  3. Update all of your imports of query and mutation from convex-dev/server to convex/_generated/server.

If you don’t want to use code generation, you can still use the untyped versions of all of these functions:

Breaking: db.update renamed to db.patch

We’ve renamed the db.update method in mutations to be called db.patch.

Rationale

The reasoning for this is that we’ve noticed some confusion around how db.update works.

db.update (now called db.patch) takes a partial version of a document and updates the specified fields to the new values. It doesn’t edit fields on the document that aren’t specified. For example you can create a document and update a single field on it with db.patch:

const id = db.insert("messages", {
  message: "hello",
  author: "Alex"
});
db.patch(id, {
  message: "Hi!"
});
// The document still has the original author field and the new message.

This is in contrast to db.replace which will replace an entire document with a new object, overwriting all fields.

Migration

Switch all of the usages of db.update to use db.patch. If you find call sites that are setting all the properties of a document, you probably want db.replace instead.

TypeScript Type Checking

The Convex CLI will now run attempt TypeScript type checking on your Convex functions during npx convex push and npx convex codegen. This should help catch potential bugs early on and produce more readable error messages.

Extra Goodies

We also have a lot of small improvements that should make developing on Convex better than ever:

  • There is now Next.js documentation that teaches you how to set up Convex in a Next.js app.
  • The Convex CLI now updates your generated code during npx convex push so it doesn’t fall out of date.
  • Convex now works on Safari 14! It will also work on older versions of Safari as long as none of your functions use the bigint type.
  • If you forget to include the ConvexProvider in your app, there is now a helpful error message.