Database support

Define your data models once, and Serverpod generates the Dart classes, the database schema, the migrations, and a type-safe query API on top of Postgres or SQLite. No SQL to hand-write, no mapping layer to keep in sync.

Get Started

One language across your stack

On a typical stack, your Flutter app is written in Dart, and your database is queried in SQL, with a mapping layer in between that you maintain by hand. When the schema changes, you update the models, queries, and migrations separately, and hope they still agree. That gap between the two sides is a common source of bugs.

Serverpod removes that gap. You describe your data once, and the framework generates the table, the migration, and the query API from it. Because queries are type-checked when you compile, referencing a column that does not exist or comparing the wrong type fails to build, rather than failing in production. The same types carry through all the way from the database row to your Flutter widget.

From model to widget in three steps

1Define your model

Add the table key to a model file, and Serverpod manages the id column for you, as an int or a UUID.

class: Company
table: company
fields:
  name: String

2Run serverpod start

serverpod start runs your server, watches your models, and regenerates the Dart class, the table definition, and the full API as you save. When the schema changes, press M in its terminal to create the migration and A to apply it.

serverpod start

3Use the typed result in Flutter

The Company class exists on the server and in your Flutter app, with no serialization code in between.

// Server: an endpoint that queries the database.
class CompanyEndpoint extends Endpoint {
  Future<List<Company>> list(Session session) =>
      Company.db.find(session);
}

// Flutter: the same typed Company, straight from the database row.
var companies = await client.company.list();

Query with a type-safe API

Filters are built from typed column descriptors and checked at compile time. Logical operators (& for and, | for or, ~ for not), like and ilike, between, inSet, comparisons, and count are all supported.

var alice = await User.db.find(
  session,
  where: (t) => t.name.equals('Alice') & (t.age > 25),
);

Relations without the boilerplate

Serverpod supports one-to-one, one-to-many, many-to-many, and self-relations, along with referential actions. You can filter across a relation and aggregate over it (any, every, none, count) in the same type-safe expression.

var powerBuyers = await User.db.find(
  session,
  where: (t) => t.orders.count((o) => o.itemType.equals('book')) > 3,
);

Migrations that track your schema

serverpod create-migration compares your models against the last migration and writes the SQL needed to move the database forward. It stops when a change would risk data loss unless you pass a force flag, and repair migrations bring a database that has drifted back in line.

serverpod create-migration

Transactions, indexes, and optimization

Group operations into atomic transactions with configurable isolation levels and savepoints. You declare indexes in the model file, from a plain unique constraint up to GIN, vector (HNSW and IVFFLAT), and spatial indexes.

await session.db.transaction((transaction) async {
  await Company.db.insertRow(session, company, transaction: transaction);
  await Employee.db.insertRow(session, employee, transaction: transaction);
});

Vector and geospatial search

The same typed query API also covers pgvector similarity search (L2, cosine, inner product, and more) and PostGIS geography types, with spatial operators such as distanceWithin, intersects, and contains.

var similar = await Document.db.find(
  session,
  where: (t) => t.embedding.distanceCosine(queryVector) < 0.5,
  orderBy: (t) => t.embedding.distanceCosine(queryVector),
  limit: 10,
);

The same database, on the device

Mark a model with database: client and your Flutter app gets a local SQLite database with the same ORM interface as the server, including relations and transactions. Postgres handles production-scale workloads on the server; SQLite handles on-device and lightweight workloads.

var session = await client.createSession(path);
var pending = await Task.db.find(
  session,
  where: (t) => t.done.equals(false),
);

Everything included

Type-safe query builder
Automatic migrations and repair migrations
One-to-one, one-to-many, and many-to-many relations
Self-relations and referential actions
Filtering across relations (any, every, none, count)
Indexes: unique, GIN, HNSW, IVFFLAT, and every Postgres index type
Transactions with isolation levels and savepoints
Pagination, sorting, and batch operations
Raw SQL access when you need it
pgvector similarity search
PostGIS geospatial queries
On-device SQLite database for Flutter
Postgres and SQLite backends

Why Serverpod

Full-stack type safety. One language from the database row to the Flutter widget, checked at compile time.
Open source. You keep control of your stack with no lock-in.
Zero-configuration deployments. Ship to Serverpod Cloud without touching a config file.

Works with

Postgres PostGIS pgvector SQLite

Frequently asked questions

Does Serverpod work with Postgres?

Yes. Postgres is the default backend, and a local Postgres instance is set up for you when you create a new project.

Can I use the database offline in my Flutter app?

Yes. Your Flutter app gets a local SQLite database with the same query API as the server, so it reads and writes fully offline and on device.

Is the ORM type-safe?

Yes. Queries are built from typed column descriptors and checked at compile time, so a wrong column or type is caught before the code runs.

How do database migrations work?

serverpod create-migration compares your models to the last migration and generates the SQL to update the schema. It guards against accidental data loss, and repair migrations resync a database that has drifted.

Can I write raw SQL when I need to?

Yes. Alongside the generated API, you can run explicit SQL queries through the session's database access.

Does it support vector search for AI and embeddings?

Yes, on Postgres. Vector fields support pgvector similarity search (L2, cosine, inner product, and more) with HNSW and IVFFLAT indexes. Vector search is not available on the SQLite backend.

Does it support geospatial queries?

Yes. PostGIS geography types support spatial filters and ordering, including distance, intersection, and containment.

Does it create foreign keys and relations for me?

Yes. Declaring a relation in a model generates the foreign keys and keeps the related data in sync, with support for referential actions.

Stay up-to-date

Our mailing list keeps you up-to-date with new Serverpod releases and features. You will get an email about once a month or when something big is happening. We promise to keep it relevant and we have a strict no-spam policy.

© 2026 Serverpod AB
Built with Serverpod - Hosted on Serverpod Cloud