Skip to Content
DocumentationDataGuidesSet Up the Database

Set Up the Database

Momen provides a PostgreSQL-backed relational database. This guide covers adding tables and fields, configuring relations between tables, unique constraints, data permissions, and vector storage.

Data model overview

Open the Data tab → Data model to view and edit your project’s data model.

The left sidebar lists every table in the project under two groups, Custom and System. Selecting a table shows its details on the right, organized into five tabs:

TabContents
FieldThe table’s fields, and where you create, edit, and delete them.
ConstraintUnique constraints on the table — see Constraint settings.
PermissionCRUD permissions for the table — see Permission management.
Vector storageVector storage settings for text fields — see Set Up Vector Search.
TriggerTriggers that reference this table.

The icons to the right of the table name switch between the detail view and the relationship view. The detail view shows and edits a single table; the relationship view shows every table and how they relate.

Relationship view

Table name, field name, and API name

Every table and every field carries two names:

  • Table name / field name — the label shown in the editor, meant to be read by people. It must not contain spaces, must be unique, and is limited to 60 bytes (about 20 CJK characters or 60 Latin characters).
  • API name — the unique identifier the system uses to access the table or field. It is generated from the table or field name when you create it and can be edited beforehand, but cannot be changed once created. An API name must start with a lowercase letter and may contain only lowercase letters, digits, and underscores (e.g. blog_article), is limited to 63 characters, and cannot start with fz_ (a prefix reserved for the system).

These strings cannot be used as API names: add, alter, all, and, any, as, asc, between, case, check, column, constraint, create, database, default, delete, desc, distinct, drop, exec, from, having, in, index, join, like, limit, not, or, procedure, rownum, select, set, table, top, union, unique, update, values, view, where, bigint, timestamp, timestamptz, timetz, date, numeric, uuid, jsonb

System tables and system fields

Besides your own tables and fields, a project also contains system tables and system fields maintained by platform features.

System tables

System tables are maintained by platform features. Except for the account table, system table API names start with fz_, and they do not accept custom fields or structural changes. No system table can be deleted.

NameAPI namePurpose
accountaccountAccount records. Supports custom fields and relations. The real credentials used for sign-in live in an internal credential table; the matching fields on the account table are synced copies you can use in your app.
role tablefz_permission_roleThe roles defined in the project.
user rolefz_account_has_permission_roleWhich accounts hold which roles.
conversationfz_conversationAI agent conversations.
messagefz_messageMessages within a conversation.
message_contentfz_message_contentThe content of a message (text, image, JSON, and so on).
tool_usage_recordfz_tool_usage_recordRequests and responses of AI agent tool calls.
provincefz_provinceProvince data.
cityfz_cityCity data.
districtfz_districtDistrict data.
audit recordfz_audit_recordRecords of admin operations.

The login password, along with the username, email, and phone number used to verify sign-in, are all stored in an internal credential table that is not exposed and cannot be accessed by your app. The identically named username, email, and phone number fields on the account table are synced copies maintained by the platform, meant for querying and display; rewriting a copy does not change the user’s sign-in credentials.

The remaining system tables are written by their platform features. Running an AI agent, for example, produces conversation and message records. You cannot add rows to these tables by hand in the database editor.

System fields

Every table — custom and system alike — is created with three system-managed fields. Momen maintains them directly; they cannot be edited, deleted, or renamed.

FieldAPI nameTypeBehaviour
ididBigIntThe row’s primary key, guaranteeing uniqueness. Auto-incremented on every insert.
created_atcreated_atTimestamptzWhen the row was first inserted into the database.
updated_atupdated_atTimestamptzWhen the row was last updated. Maintained by the database whenever the data actually changes.

The database guarantees that id is unique and increasing, but not contiguous. All of the following create gaps:

  • Deleting a row — ids of new rows skip the deleted one
  • Transactional tasks — ids consumed during the transaction are not returned on rollback
  • Bulk imports — ids are pre-allocated for every row, so skipped rows or a failed, rolled-back import leave gaps

For that reason, do not use id as a user-facing order number or membership number that must be contiguous or follow a fixed format. It remains fine for referencing rows and querying specific records.

Configure tables

Add table

Click + to the right of the search box in the sidebar to open New table, then configure:

  • Table name — the name shown in the editor.
  • API name — the unique identifier the system uses to access the table. Generated from the table name by default and cannot be changed after creation.
  • Continue to create — keep the dialog open after clicking Create, so you can add several tables in a row.

New table

Add fields

Select a table, open the Field tab, and click New field. A configuration panel opens on the right. Configure:

  • Name — the name shown in the editor.
  • Type — pick from the Basic, Relation, and Enum groups. Basic types include Text, Decimal, BigInt, Boolean, Timestamptz, Timetz, Date, JSON, Image, Video, and Function — see Data Types for the full reference.
  • Required — when on, the field cannot be empty.
  • Default value — the value written when a new row leaves the field empty.
  • API name — the field’s unique identifier. Generated from the name by default and cannot be changed after creation.
  • Continue to create — keep the panel open after saving, so you can add several fields in a row.

The field list also shows each field’s type, required flag, default value, and API name. Changes that are not yet synced are marked with a dot at the start of the row, and the table header shows a Not synchronized badge.

Field list

Turning Required on for a field that is already synced and has no default value opens an Input default value dialog; you must supply a default before you can continue. After syncing the backend, every existing empty value in that field is automatically updated to this default.

Momen uses Decimal for precise numbers. Custom code (JavaScript) or float APIs can introduce precision loss (e.g. 0.1 + 0.2 → 0.30000000000000004) if written back to the database.

Add relations

When creating a field, switch its Type to Relation and pick the target table. For example, relate the “BlogArticle” table to the “account” table to record each article’s author.

Configure:

  • Name — the name of this relation in the current table (e.g. Author).
  • Type — the target table of the relation.
  • Relation — N:1 or 1:1. The panel then spells out what the relation means in one sentence, e.g. “A BlogArticle can only have one account, but an account can have multiple BlogArticle”.
  • Relationship name in the target table — the name the reverse relation takes in the target table (e.g. Articles in the account table).

On save, a foreign key field named <Name>_id is created in the current table with a foreign key constraint pointing to <target table>.id. In the field list, the foreign key and the relation field appear grouped, with the foreign key tagged Foreign key.

Relation field

Many-to-many relations need a junction table: create one, then add an N:1 relation from it to each of the two tables. See Relational Data Modeling for the modeling approach.

Sync changes

  • Once tables, fields, and relations are configured, click Sync changes in the top bar.
  • The dialog lists what the update includes (e.g. Data model, Permission); confirm by clicking Sync changes.
  • Schema changes take effect only after the sync completes, and the Not synchronized badge clears.

Sync changes

⚠️

If the project is already published, schema changes can break online requests or destroy data — proceed carefully. Once you delete a field or a table and sync, its data is deleted.

Constraint settings

Unique constraints prevent duplicate values on a single field or on a combination of fields.

Open constraint settings

Select a table and switch to Constraint. Every table has a default constraint on id (e.g. article_tag_id_key) that cannot be deleted.

Add constraint

Click New constraint and fill in:

  • Name — the constraint name. Must use lowercase letters and underscores and be unique within the project, e.g. uq_article_tag.
  • Composite unique columns — the fields covered by the constraint. One field means single-column uniqueness; several fields mean composite uniqueness.

For example, combining “Title” and “Author” stops one author from posting two articles with the same title, while different authors can still reuse a title.

New constraint

Sync changes

  • After saving the constraint, click Sync changes.
  • The combined content of the constrained fields in any single row cannot exceed 8,191 bytes. UTF-8 characters take a varying number of bytes, so the number of characters you can enter is not fixed.
  • If the table already contains duplicate rows that violate the constraint, the sync fails. Clean up the conflicting data in Manage Data Records first, then sync again.
  • If a constrained field has a default value, that default can conflict with the constraint; the editor warns you when saving.
  • Constraints cannot be edited after syncing. To change one, delete the original constraint, sync, then create a new one.

Permission management

Momen controls which data each user can read or operate on through role permissions and data permissions. Table-level permissions are configured directly on the data model’s Permission tab:

  • Tabs at the top switch between roles (Logged-in user, Anonymous user, …), configuring that role’s permissions on this table.
  • The grid lists every field against Read, Insert, Update, Delete, Total, and Enable sum/avg/max/min. Tick fields individually, or use the column-header toggles to switch a whole column on or off.
  • The funnel icon next to each operation configures the row filter for that operation (data permissions).
  • More permission configuration at the top right opens the full permissions page.

Table permissions

Permission changes also take effect online only after Sync changes. For the full permission configuration, read Permissions.

Vacuum full

After large deletes or updates, PostgreSQL does not release the disk space right away. Vacuum full, to the right of the table name, physically reorganizes the table to reclaim that storage.

⚠️

The table is locked while the operation runs: all reads and writes (submitting data, querying, and so on) are unavailable until it finishes. Run it only during low-traffic windows.

Vacuum full

Last updated on