FumaDB

Defining Schema

You can define FumaDB schema similar to Drizzle ORM.

Table Definition

Use table(name, columns) to define a table. Columns are defined as an object, where each key is the column name.

import { table, column, idColumn } from "fumadb/schema";

const users = table("users", {
  id: idColumn("id", "varchar(255)").defaultTo$("auto"),
  name: column("name", "string"),
  email: column("email", "string").nullable(),
});

Columns

The first argument is database name.

column("content", "varchar(200)");

While FumaDB uses column name to interact with ORMs and MongoDB, database name defines the underlying name for SQL database.

You can mark columns nullable:

column("email", "string").nullable();

For default value, FumaDB supports database-level default value:

column("email", "string").defaultTo("hello world");

Or dynamically generated value:

idColumn("id", "varchar(255)").defaultTo$("auto");
idColumn("timestamp", "date").defaultTo$("now");

idColumn("name", "string").defaultTo$(() => myFn());

Good to Know

FumaDB always prefer generating default values on-demand to avoid database inconsistencies.

Unique Constraint

Unique constraints also allow duplicated null values even for MongoDB.

To define a column-level unique constraint:

column("email", "varchar(255)").unique().nullable();

Or composite unique constraint:

import { table, column, idColumn } from "fumadb/schema";

const users = table("users", {
  id: idColumn("id", "varchar(255)").defaultTo$("auto"),
  name: column("name", "string"),
  email: column("email", "string").nullable(),
}).unique("name_email_uk", ["name", "email"]);

ID Columns

You can have only one ID column per table, its type can be:

  • varchar(n) (recommended: varchar(255)).

For ID columns, you can define auto-generated ID columns with default set to auto. FumaDB will generate it using CUID or database's built-in function.

Why no multiple ID columns?

This is because MongoDB doesn't support composable primary keys.

Relations

Relations are defined in the relations field of the schema.

import { schema } from "fumadb/schema";

export const v1 = schema({
  version: "1.0.0",
  tables: { users, posts },
  relations: {
    users: ({ many }) => ({
      // implicit
      posts: many("posts"),
    }),
    posts: ({ one }) => ({
      author: one("users", ["user", "id"]).foreignKey({
        // you can define actions for `onDelete` and `onUpdate`
      }),
    }),
  },
});

Additionally, you can call imply() to disambiguate relations.

import { schema } from "fumadb/schema";

export const v1 = schema({
  version: "1.0.0",
  tables: { users, posts },
  relations: {
    users: ({ many }) => ({
      posts: many("posts"),
    }),
    posts: ({ one }) => ({
      author: one("users", ["user", "id"]).foreignKey().imply("posts"),
    }),
  },
});
  • one(targetTable, ...[field, reference]): Defines a one-to-one or many-to-one explicit relation.

For each explicit relation, you can declare an implicit relation in the referenced table.

  • one(targetTable): Defines a one-to-one implicit relation.
  • many(targetTable): Defines a one-to-many implicit relation.

Foreign key required

For explicit relations, .foreignKey() is required due to the limitations of Prisma.

Schema Variant

You can create schema variant based on another schema, useful for extending existing schemas.

import { schema, table, column, idColumn, variantSchema } from "fumadb/schema";

export const v1 = schema({
  version: "1.0.0",
  tables: {
    users: table("users", {
      id: idColumn("id", "varchar(255)"),
      name: column("name", "string"),
    }),
  },
});

export const v1WithAdmin = variantSchema("with-admin", v1, {
  tables: {
    admin: table("admin", {
      id: idColumn("id", "varchar(255)"),
    }),

    // replace
    users: table("users", {
      id: idColumn("id", "varchar(255)"),
      email: column("email", "string"),
    }),
  },
});
  1. you can add & replace tables and relations.
  2. you cannot remove tables, otherwise it may breaks original relations.
  3. when replacing tables, its original relations will be removed.

The schema will be available as 1.0.0-with-admin, consumer can choose it over 1.0.0.