Site databases
Every project can have its own Postgres database, created and shaped by the AI.
On this page
A site database is a real Postgres database that belongs to one project. Your published site reads and writes it at runtime, and you browse and edit the contents from the CMS tab.
It is not a spreadsheet or a proprietary content store. It is Postgres, with tables, columns, foreign keys, constraints and triggers.
Getting a database
Projects start without one. Open the CMS tab in the editor toolbar and you will see an empty state that says No database with the description "Ask the AI to create a database." and a single Ask AI button.
The button drops the prompt "Create a database for me." into chat. You can also just describe what you want to store, and the AI provisions the database itself before it starts building:
Add a blog with posts, authors and tags. Posts need a title, cover image, body and publish date.Provisioning takes a few seconds. When it finishes, the connection string is available to your site and the CMS tab switches from the empty state to the collections browser.
How the AI creates tables
The AI has direct SQL access to your database and uses it to create tables, add columns, write indexes and constraints, run migrations and insert seed data. It follows a fixed set of conventions so the CMS can read the result:
- Every table gets a generated UUID primary key:
id uuid PRIMARY KEY DEFAULT gen_random_uuid(). Auto incrementing integer ids are not used. - A human readable URL key lives in a separate
slugcolumn, never as the primary key. - Relations point at the target table's
id, so foreign key columns areuuidtoo. - Timestamps are named exactly
created_atandupdated_at, typedtimestamptz.created_atis set by its default, andupdated_atis re-stamped on every edit by a trigger created alongside the table.
The CMS treats id, created_at and updated_at as system managed. They show in the row editor as read only, and on a new row they read "Auto-generated".
Field metadata
Some things Postgres cannot express on its own. A column that stores an image URL is just text as far as the database is concerned. Modulify keeps that extra information in a table called _collection_fields, which the AI writes to in the same migration that creates the column.
That metadata is what makes a column render as a color swatch, a rich text editor, a file uploader, an image uploader, a multi reference picker, or one field with language tabs. _collection_fields is never shown as a collection of its own. See Column types for what each one looks like.
Running SQL
SQL runs through the AI, not through a console. There is no query editor in the product: you ask for the change in chat and the AI runs the statement for you.
Add a "featured" boolean to posts, defaulting to false, and set it true for the three newest posts.The SQL tool has real limits. A single statement is capped at 20,000 characters, each statement gets a 15 second timeout, and a result set comes back to the AI capped at 200 rows. Long running work has to be broken into steps.
When the AI runs a statement that changes the schema, the CMS refreshes on its own, so a new collection appears in the sidebar without a reload.
How your site reads the database
The connection string is stored as an encrypted project secret named DATABASE_URL and injected into your site's environment. There is one value, shared by the preview, development and production machines.
Read it from server side code with any Postgres client. The AI typically installs pg, drizzle-orm or kysely and reads the value from process.env:
const connectionString = process.env.DATABASE_URLThe value is never hardcoded into the codebase, and it is never read from the browser.
The DATABASE_URL secret is locked
DATABASE_URL appears in the Secrets tab with a Managed badge. Hovering it says "This secret is managed by Modulify and cannot be edited or removed." You can reveal and copy the value, but you cannot change or delete it, and neither can the AI. It lives and dies with the database itself.
What you can and cannot do
You can browse every collection, page through rows, search them, create rows, edit them, duplicate them, delete them, clear a collection, export data, and toggle whether system collections are visible.
You cannot add, rename or drop a column from the CMS. You cannot create a collection by clicking a button, run your own SQL, connect an external Postgres database, or see database user credentials. Every schema change goes through chat.
When the database is unavailable
If the database is not reporting as running, the CMS shows Database unavailable with "Try again in a few moments." If Modulify cannot reach the database service at all, the message is "Could not reach the database service. Your data is safe, please try again in a moment!" Neither affects your data. Use the refresh button in the Collections header to retry.
From an AI client over MCP
get_site_database reads the connection details and create_site_database gives a site a database if it does not have one, which is safe to call even when it already does.
Running arbitrary SQL is not available over MCP. A connected client works through the CMS tools instead, or asks the site AI to run a migration for it.
See MCP tools.
Next
- The CMS walks through browsing and editing your content.
- Column types lists every Postgres type and the editor it gets.
- Clearing and exporting covers getting your data out and wiping it.