Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 49 additions & 71 deletions docs/02-overview.md
Original file line number Diff line number Diff line change
@@ -1,102 +1,80 @@
---
sidebar_label: Overview
sidebar_label: How it works
sidebar_class_name: sidebar-icon-overview
---

# Overview
# How Serverpod works

Serverpod is an open-source backend framework for Flutter applications written in Dart. It aims to minimize boilerplate and integrate many common backend tasks out of the box. With Serverpod, Flutter developers can build secure, scalable server applications using the same language and tools as their client apps, benefiting from seamless code generation and a rich set of built-in capabilities.
This page explains the mental model behind Serverpod — the project structure, how code generation works, and what happens when your Flutter app calls the server. It is a complement to the [get-started tutorial](./01-get-started/01-creating-endpoints.md), which teaches by doing. This page explains the why and how underneath.

## Key capabilities
## Project structure

- **Automatic code generation:** Serverpod analyzes your server code and automatically generates the client-side Dart API and data classes. Calling a remote endpoint becomes as simple as calling a local method.
- **World-class logging:** Built-in logging and monitoring tools allow you to pinpoint exceptions and slow database queries through an easy-to-use interface.
- **Built-in caching:** High-performance, distributed caching is included. Any object can be cached (primitives, lists, maps, and serializable models) in memory on the server or distributed using Redis.
- **Easy-to-use ORM:** Serverpod provides an ORM that uses native Dart types and null-safety for database queries. You write Dart code instead of SQL, and Serverpod builds your PostgreSQL queries under the hood. The ORM has an intuitive Dart-first API for relations and joins.
- **Database migrations:** A built-in migration system helps keep your database schema in sync as your project evolves. You can version schema changes and apply them easily during deployment.
- **File uploads:** First-class support for file uploads to cloud storage or the database. Files can be stored in Amazon S3, Google Cloud Storage, or even in your PostgreSQL database.
- **Authentication:** User authentication comes ready out-of-the-box. Serverpod supports sign-in with Google, Apple, Firebase, email/password, or custom authentication mechanisms.
- **Real-time data streaming:** Support for real-time communications via Dart streams. You push serialized objects over secure WebSocket connections, enabling live updates (e.g., for chats and games).
- **Task scheduling:** Serverpod's future calls replace complicated cron jobs. Call a method anytime in the future or after a specified delay. The calls persist even if the server is restarted.
- **Deployment automation:** Serverpod Cloud (currently in private beta) allows you to deploy your server with zero configuration. There are also community-supported Terraform scripts for deployment on Google Cloud Platform and AWS, making it quick to provision infrastructure and deploy your server. Your Serverpod project comes with a Docker container for flexible deployment options.
- **Built-in web server:** Serverpod comes with a modern, high-performance web server called [Relic](https://docs.dartrelic.dev/). It is built to meet the demanding requirements of modern web applications and provides a robust, type-safe, and efficient foundation for building scalable web services, such as REST APIs, webhooks, and web pages.
When you run `serverpod create`, it produces three Dart packages in a single workspace:

## Defining Endpoints

In Serverpod, endpoints are the entry points that clients call to execute server-side logic. An endpoint is defined by creating a class that extends the Endpoint class and adding asynchronous methods to it. Each endpoint method must return a `Future<Type>` and take a `Session` object as its first parameter. The `Session` provides context about the call and gives access to server resources like the database or cache.

For example, here's a simple endpoint definition with a single method:

```dart
import 'package:serverpod/serverpod.dart';

class GreetingEndpoint extends Endpoint {
Future<String> hello(Session session, String name) async {
return 'Hello $name';
}
}
```text
my_project/
├── my_project_server/ # Your server-side code — endpoints, business logic, models.
├── my_project_client/ # Auto-generated. Never edit by hand.
└── my_project_flutter/ # Your Flutter app.
```

You can place your endpoints anywhere in your server package. After adding or modifying endpoints, you run the Serverpod code generator (`serverpod generate`) to update the client interface. The generator produces a Dart client library that mirrors your server API.

On the Flutter client side, calling the endpoint is as straightforward as calling a local function. For instance, using the generated client, you can invoke the above hello method like this:
The split is intentional. The `_server` package runs on your infrastructure and can safely hold secrets, database access, and privileged logic. The `_flutter` package runs on the user's device and must never contain those things. The `_client` package is the bridge — it is generated automatically from your server code and gives the Flutter app a typed Dart API to call the server with, without exposing any server internals.

```dart
final result = await client.greeting.hello('World');
```
Because `_client` is always generated and never edited manually, it stays in sync with the server by construction. You do not write serialization code, HTTP calls, or API contracts by hand.

Serverpod handles the network communication and data serialization behind the scenes. Under the hood, it uses JSON serialization and HTTP or WebSocket calls, but as a developer, you only see calls using the typed Dart interface. For more details on defining and using endpoints, see the [Working with endpoints](./concepts/working-with-endpoints) guide in the documentation.
## Code generation

## Data Models and Serialization
Serverpod's code generator reads two kinds of source files from the `_server` package:

Serverpod makes it easy to define the data models that your server will use in its API and when talking with the database. You define serializable data models in YAML files (with a `.spy.yaml` extension, short for _Serverpod YAML_). These model definitions are used to generate Dart classes that are shared by the server and the app, ensuring a consistent data schema on both sides. If a database table is associated with the model, the code generator will also produce the necessary database integration code.
- **Endpoint classes** — Dart files that extend `Endpoint`. Each public method on an endpoint class becomes a callable method in the generated client.
- **Model files** — YAML files with a `.spy.yaml` extension that define your data classes, enums, and exceptions.

A simple model definition might look like this:
Running `serverpod generate` produces output in two places:

```yaml
class: Company
fields:
name: String
foundedDate: DateTime?
```mermaid
flowchart TD
A["Endpoint classes (.dart)"] --> G
B["Model files (.spy.yaml)"] --> G
G(["serverpod generate"])
G --> C["_client — typed Dart API"]
G --> D["_server — serialization + ORM"]
```

This defines a `Company` class with two fields. When you run `serverpod generate`, Serverpod creates a Dart class named `Company` (with a `name` and `foundedDate` property) that can be used in your endpoint methods and in the client code.

By default, model classes are plain data holders that can be sent over the network. Serverpod supports most basic types, including `bool`, `int`, `double`, `String`, `Duration`, `DateTime`, `ByteData`, `UuidValue`, `Uri`, and `BigInt`. You can also use `List`, `Map`, `Set`, and other custom serializable objects. Null safety is supported, and the models can be nested with each other as needed.
The `_client` package gets a typed Dart class for every endpoint and a matching Dart class for every model. The `_server` package gets serialization logic and, for models that declare a database table, the ORM interface.

## Database Integration and ORM
You run `serverpod generate` whenever you add or change an endpoint method or a model file to update the `_client` package with the new changes.

If you want a model to also correspond to a database table, you simply add a table name to the YAML definition. For example, to back the `Company` class with a database table, you could write:
## Request lifecycle

```yaml
class: Company
table: company
fields:
name: String
foundedDate: DateTime?
```
When your Flutter app calls a server method, several steps happen transparently:

Including the `table` key tells Serverpod to set up a PostgreSQL table named `company` for this model and generate an ORM interface for it. After running `serverpod generate`, the `Company` Dart class will include additional capabilities for database operations.
```mermaid
sequenceDiagram
participant App as Flutter app
participant Client as Generated client
participant Server as Serverpod server
participant Endpoint as Your endpoint

Working with a database in Serverpod is straightforward and fully integrated. By default, Serverpod uses a PostgreSQL database to store persistent data. Connection settings (such as host, port, database name, user, and password) are configured in your project's YAML files under the `config/` directory. New Serverpod projects come pre-configured for a local PostgreSQL instance, so you can usually start a development server without additional setup.
App->>Client: client.endpoint.method(args)
Client->>Server: HTTP request
Server->>Endpoint: method(session, args)
Endpoint-->>Server: result
Server-->>Client: HTTP response
Client-->>App: typed Dart object
```

When a data model is bound to a database table (via the table field in the model definition), Serverpod's code generation provides an Object-Relational Mapping (ORM) for that model. Each generated model class gains a static `db` field that offers convenient methods for common database operations (CRUD). For example, you can insert a new record and then query it as follows:
From the Flutter app's perspective, calling the server looks identical to calling a local Dart method. The generated client handles serialization, the HTTP transport, and deserialization of the response. Your endpoint method receives the arguments as ordinary Dart values and returns an ordinary Dart value.

```dart
// Insert a new Company row into the database
var company = Company(name: 'Serverpod Inc.', foundedDate: DateTime.now());
company = await Company.db.insertRow(session, company);
### Real-time streaming

// Retrieve the company by its id
var storedCompany = await Company.db.findById(session, company.id);
```
Regular endpoint methods follow the request/response lifecycle above. For real-time use cases — live updates, collaborative features, multiplayer — Serverpod also supports [streaming endpoints](./06-concepts/15-streams.md), which keep a WebSocket connection open and let server and client push data to each other continuously.

In the above snippet, `insertRow` will write a new row to the `company` table and return the inserted object with its `id` (primary key) populated. The `findById` call then fetches that row by its `id`. All database calls are asynchronous and use the same `Session` that is provided to your endpoint methods, ensuring they are executed in a transaction/context that you control. You can also perform more complex queries using Serverpod's fluent query interface – for example, searching with filters, joining relations, sorting, or even writing raw SQL if needed.
### Session

The ORM layer is fully type-safe; query filters and results are expressed in terms of your Dart classes and fields, preventing mistakes like SQL syntax errors or mismatched column types at compile time.
The `Session` parameter that every endpoint method receives is Serverpod's request context. It gives the method access to the database, cache, authenticated user information, and logging — scoped to the lifetime of that single request. It is not a singleton; each call gets its own `Session` instance.

Serverpod's migration system further simplifies database work by allowing you to apply schema changes. Whenever you alter your models, you can generate a new migration by running `serverpod create-migration`. Have Serverpod apply it on the next server startup by passing the `--apply-migrations` flag when starting the server. This helps keep the database schema versioned and in sync with your code. See the documentation's [Database section](./concepts/database/connection) for details on writing complex queries, transactions, and managing migrations.
## Type safety across the stack

## Conclusion
Serverpod's model files (`.spy.yaml`) are the single source of truth for your data shapes. When you run `serverpod generate`, the same Dart class is generated in both `_server` and `_client`. This means the object your endpoint returns and the object your Flutter app receives are the same type — not two hand-maintained copies that can drift apart.

Serverpod provides a robust, full-stack solution for Flutter backend development. Its high-level architecture (endpoints for RPC, YAML-defined serializable models, and an integrated PostgreSQL-backed ORM) allows teams to move quickly and safely when building out server features. Many auxiliary concerns – from caching and authentication to logging and deployment – are handled by Serverpod's built-in modules, reducing the need for additional services and glue code. This concise overview covered the basics; as a next step, you can explore the in-depth documentation on specific topics such as endpoints, database usage, or advanced features like streams and authentication to evaluate how Serverpod fits your project's needs. A great way to learn Serverpod is also to go through our [Get started guide](./01-get-started/01-creating-endpoints.md).
This eliminates an entire category of bugs common in traditional client-server development: mismatched field names, wrong types, forgotten null checks after an API change. When you rename a field in a model file and regenerate, the compiler immediately surfaces every place in both the server and the app that needs updating.
Loading