RJR
Convex Community3y ago
19 replies
RJ

Declarative schema validations and branding

I'm sure this is a big topic that you all have thought a lot about, but I've found myself more and more wishing that I could declare constraints for specific fields in my schema. It would be really neat (I think) if Convex supported something like this:

items: defineTable({
  quantity: s.number().validate((n) => n >= 0)
})


Such that whenever using
db.insert
,
db.patch
or
db.replace
, any validations would be run on new or modified fields, and an error would be raised if any fail (or, better yet, the validation failure is [even optionally] reflected in the type signature).

I imagine there are much fancier alternatives to this, but this seems to have some nice advantages nonetheless:

1. It's effectively the same as what a user would have to do within each of their individual mutation functions if they're trying to enforce this invariant in a more ad-hoc way (the only option at the moment). By "effectively the same" I mean it is computationally identical (JS/TS code being executed at runtime), but a much nicer API.
2. It's what many ORMs do and have done for a long time (e.g. Ruby's Rails and Elixir's Phoenix, at least)—which implies a) that it's generally a useful pattern and also b) that it's familiar to people.
3. It doesn't require changing the database layer to implement (obviously database-layer guarantees are much nicer, but are, I imagine, also much harder—or at least more work).

In a similar vein, I've often found myself wanting to be able to more easily declare that e.g. a
s.string()
is an ISO 8601 date. It would be cool to extend the above with type branding, such that you could have something like:

export default defineSchema({
  items: defineTable({
    quantity: positiveInteger
  })
});

const positiveInteger = s.number()
  .validate((n) => n >= 0)
  .brand("PositiveInteger");

export type PositiveInteger = Infer<typeof positiveInteger> // number & Brand<"PositiveInteger">


These would both be huge DX upgrades for me!
Was this page helpful?