Wonderful Dart Full-Stack Experience: Serverpod Framework Designed Specifically for Flutter
Dart backend is a powerful option.
When an application begins to have more complex interactions or needs to store and process large amounts of data, relying solely on the Flutter front-end is no longer sufficient. At this point, Flutter developers often need to rely on servers to manage more complex features, especially for applications like note or chat apps, which require storing data in the cloud. Many developers choose serverless services like Firebase, and while this solution is indeed convenient, it can not be enough when encountering complex calculations that need to be performed on the server.
In such situations, Serverpod provides an ideal choice for Flutter developers. It’s known as ‘The missing server for Flutter’ and is a complete backend framework specifically designed for Flutter. It allows writing server-side code in Dart and automatically generates APIs and data access, enabling Flutter projects to seamlessly connect to backend requirements and providing a consistent development experience.
Scalable servers like Serverpod are designed to handle increasing demands while maintaining stable performance. This scalability ensures fast response times under high load conditions, improving user experience and streamlining the development workflow.
Official:https://serverpod.dev/
Installation:https://docs.serverpod.dev/
Package:https://pub.dev/packages/serverpod
Why choose Serverpod for backend development? Key features to know:
Serverpod Features and Functionality#
Consistent Development Experience#
Serverpod uses Dart for both frontend and backend development, providing a consistent development experience by allowing developers to write the entire application in a single language.
This eliminates the context switching between different programming languages, significantly improving development efficiency.
Code Sharing and Reuse#
Serverpod promotes code sharing and reuse between the frontend and backend, enabling the sharing of data models, service implementations, exception definitions, data validation rules, and even business logic.
This streamlines the development workflow, reduces code duplication, lowers development costs, and fosters a consistent and easily maintainable codebase.
Sound Static Typing#
Serverpod leverages Dart’s sound static typing system to provide a type-safe development environment. This ensures that potential errors are caught during compilation, minimizing the risk of runtime issues and maximizing code reliability and stability.
Code Generation#
Leveraging YAML configuration, Serverpod’s code generation automatically creates server and client code, encompassing HTTP requests, WebSockets, responses, error handling, data models, and API definitions. This significantly simplifies network communication, data serialization, and deserialization, freeing developers to concentrate on core application logic rather than intricate network implementation details.
This also ensures consistency between frontend and backend APIs. High-level APIs enable seamless frontend-backend interaction through simple function calls, mimicking local invocations.
Postgres SQL Database#
Serverpod is deeply integrated with PostgreSQL, providing a highly scalable database management solution ideal for handling large datasets and complex application requirements. Leveraging PostgreSQL’s robust features, including ACID properties and high reliability, Serverpod ensures data integrity and performance.
ORM (Object-Relational Mapping)#
Serverpod features a type-safe and null-safe Dart ORM, empowering developers to interact with the database directly through intuitive Dart APIs. This minimizes the risk of errors associated with traditional SQL queries and enhances application stability. The ORM also simplifies database migrations and provides an object-oriented approach to data manipulation.
Furthermore, Serverpod’s ORM implements connection pooling, optimizing database performance by reusing connections and minimizing the overhead of frequent connection establishment and closure.
Database Migrations#
Serverpod’s built-in migration system simplifies database schema management by eliminating the need for manual SQL scripting. Upon server startup, Serverpod automatically checks database integrity and generates migration scripts containing the necessary SQL queries when changes to data models are detected.
The migration system also supports rollbacks and version control, ensuring safe and manageable schema changes.
Efficient Caching#
Serverpod leverages built-in local and distributed caching mechanisms, such as Redis, to store frequently accessed data in memory, significantly reducing database load and improving response times. This is particularly advantageous for relatively static data, allowing applications to deliver information with near-instantaneous API responses. Serverpod also provides flexible cache invalidation strategies and configuration options to fine-tune caching behavior.
Data Streaming#
Serverpod simplifies real-time communication and data synchronization, making it an excellent choice for applications like games and chat platforms. By allowing backend API functions to directly return a Stream for the frontend to consume,
Serverpod eliminates the complexities of manual WebSocket management and facilitates the easy implementation of bidirectional data synchronization. Serverpod’s data streaming is optimized for low latency and efficient handling of numerous concurrent connections.
Social Auth Support#
Serverpod provides comprehensive social authentication support, enabling users to log in using popular platforms like Google, Apple, and Firebase, alongside traditional email/password authentication. Full integration with Google APIs allows seamless access to services like Google Calendar.
Serverpod supports OAuth 2.0 and also provides the flexibility to implement custom authentication methods.
File Upload#
Serverpod seamlessly handles file uploads to leading cloud storage providers such as Google Cloud Storage (GCS) and Amazon S3.
Serverpod supports configurable file size limits, MIME type validation, and the option to store files directly in the database. Leveraging cloud storage provides high availability, scalability, and cost-effectiveness for file storage.
Task Scheduling#
Serverpod simplifies background task management with its integrated task scheduling system. Replacing complex cron jobs with intuitive Future Calls, developers can easily schedule tasks to run at specific times or after a defined delay, ensuring background processes don’t impact user experience.
Scheduled tasks are persistent and resume execution even after server restarts. The task scheduler also offers retry mechanisms and robust error handling and provides tools to monitor task execution status.
Serverpod Insights#
Serverpod Insights offers comprehensive logging, monitoring, and debugging capabilities for network and database activity. It empowers developers to quickly identify anomalies, pinpoint slow database queries, and monitor crucial system metrics such as CPU and memory utilization. This proactive approach allows for early detection and resolution of performance bottlenecks, preventing them from affecting end-users.
This proactive monitoring and debugging approach is crucial for maintaining a high-performance backend as the user base grows.
According to the Todo application example written in this article, when an error occurs in a request, Insight will list relevant information and error messages, which is very convenient for debugging.
Use graphs to understand the current number of connections, response times, CPU usage, and memory information.
VS Code Extension#
The Serverpod VS Code extension gives developers real-time feedback when writing YAML configuration files. It includes syntax diagnostics and warnings, ensuring configuration accuracy and significantly reducing the likelihood of configuration errors.
Deployment#
Serverpod provides flexible and lightweight deployment options through its support for Docker and Terraform, enabling self-hosting on any platform. Serverpod leverages Docker for containerization, enabling consistent deployments across various environments, and integrates with Terraform for Infrastructure as Code, automating the provisioning of cloud resources. Serverpod can be easily deployed to popular cloud platforms such as AWS, Google Cloud, and Azure. Developers can utilize simplified scripts to quickly deploy their applications, achieving a streamlined and automated deployment workflow.
Built-in Web Server#
Serverpod includes a built-in web server that provides basic web serving capabilities, fulfilling the needs of simple website deployments without requiring additional configuration.
However, it is important to note that this feature is currently in experimental phase and is not yet considered stable for production use.
Serverpod Cloud#
Serverpod Cloud provides a fully managed cloud infrastructure, abstracting away the complexities of managing cloud providers like Google Cloud or AWS. This simplifies deployment, reduces costs, and significantly lowers the barrier to entry for backend development. Developers can focus on building features and delivering value to their users without the overhead of managing complex infrastructure.
Key benefits include:
- Load Balancing: Automatic load balancing distributes traffic across multiple servers, ensuring high availability and responsiveness even under peak loads.
- Scalable Database: A fully managed and scalable database architecture automatically scales horizontally to accommodate growing data and traffic demands.
- Integrated File Storage: Seamlessly integrates file storage, allowing users to easily upload and manage files without configuring separate cloud storage services.
- Caching: Utilizes efficient caching technologies (e.g., Redis) with intelligent cache invalidation strategies to optimize performance and minimize database load.
- Inter-Server Communication: Enables efficient communication between servers, facilitating the development of robust and scalable microservice architectures.
- Secure Password Storage: Employs industry standards to ensure secure storage and protect user credentials.
- In-Memory State Storage: Leverages in-memory data stores for high-performance state management, ideal for real-time applications, with options for data persistence.
- Multi-Region Deployment: Supports easy configuration of multi-region deployments with automatic data synchronization to minimize latency for users worldwide.
Deployment command:
serverpod cloud deploy
It is expected to be released at the early 2025
Testing Framework#
In Serverpod 2.2, developers can write comprehensive integration tests. It contains several features:
- Request the API Endpoint directly in the test
- Easily add Seed data to the database
- Authentication of users
- Skip
tearDown()for manual release and reset, everything rolls back automatically.
The core entrance of the framework is the withServerpod() test assistant, which provides tools for automatically configuring and cleaning up the database status. By executing all tests within transactions and ensuring that each test is restarted after completion, you can achieve faster, more reliable integrated test suites.
void main() {
withServerpod('Given Example endpoint', (sessionBuilder, endpoints) {
test('when calling `hello` then should return greeting', () async {
final greeting = await endpoints.example.hello(sessionBuilder, 'Michael');
expect(greeting, 'Hello Michael');
});
});
}
Preparatory work#
Serverpod Installation#
Before installing Serverpod, make sure your system has the latest version of Dart to take advantage of all its features. Then install Serverpod CLI through the command:
dart pub global activate serverpod_cli
Version check:
serverpod version
Docker Installation#
Just download it directly from the official website. After installation, you will get the Docker CLI, which allows operations through commands in the terminal.
When executing Serverpod, you need to ensure that Docker Desktop is running at the same time. It will be responsible for starting Postgres and Redis to ensure that the server operates normally.
Additionally, it’s helpful to be familiar with YAML archives, as Serverpod relies on them for configuration. Mastering the structure and syntax of YAML can help you customize the Serverpod server according to your project needs. Also because of the use of YAML format, it can provide higher readability and scalability when supporting other programming languages or code generation in the future.
New Project#
Move to the location where you want to create the project and use the create command
serverpod create <project-name>
Project structure#
📁 API Client → xxx_client directory, which stores the program code generated from the Server side and allows the APP side to access it. It is a bridge for communication between the two ends.
📁 Flutter App → xxx_flutter directory, the actual running application
📁 Server → xxx_server directory
endpoints/→ Each file is an Endpoint, which is the connection point between Client and Server.models/→ Model definition file, use yaml to write attributes and details, and generate it through commands. Automatically corresponds to the database and can be used on both the front and back endsconfig/→ Contains service configurations in each environment, including Api Server, Web Server, Database, Redis, etc.deploy/→ Contains deployment scripts and related files for AWS and GCP
Create a “Model”#
Use yaml files to define Model Classes, which are fast to write and highly readable. In the sample Todo project, you can add a task.spy.yaml file for configuration.
class→ namefields→ data, properties. You can use the most basic Dart type, or DateTime, List<T>, Map, UuidValue, Duration, and ByteData. There are many choices.
class: Task
fields:
id: UuidValue
name: String
isDone: bool
createAt: DateTime
After completion, use serverpod to generate the Model category code to ensure security and stability.
Create a “Table”#
Model objects can be easily bound to databases, making storage and searching easier. Just add the table name to yaml and it will be the data table.
class: Task
table: task
fields:
name: String
isDone: bool
createAt: DateTime
When the relevant content of the data table is updated in the future, Migration needs to be executed to retain the original data despite changes in the database structure.
serverpod create migration
Requirement: Serverpod uses Postgres database. If the data table is not added, Serverpod will not work (unless running Serverpod mini)
Exception#
It is similar to creating a Data Model, except that it changes from class to exception and gives it the attribute field.
exception: CreateTaskException
fields:
message: String
Code Generation#
When the endpoint/ and model/ directories in the Server project are updated, and the relevant API code needs to be regenerated. You can use one line to do code generation
serverpod generate
// Monitor changes to the Server directory and
// generate code and files in real time
serverpod generate --watch
Serverpod also supports Hot Reload. When there is an update, you can directly use command + s to save it. If you run -watch mode at this time, Serverpod will generate the relevant code in real-time.
When serverpod generated is executed, Serverpod will generate Endpoint and API-related code to the protocol/ Client project’s and the Server project’s directory. Then we can easily access them from the Flutter App.
Server running#
- First, open the Docker Desktop App.
- Use the Docker command in the Server directory to start Postgres and Redis.
- Then, execute the command to start the server.
docker compose up --build --detach
dart bin/main.dart --apply-migrations
In a development environment, always using the parameter is helpful, as this will ensure that the repository is always migrated to the latest state. However, in a production environment, you should usually start the server without parameters unless you need to update or migrate to a new version.
When the database structure is updated and migrated, the server is started, and the server is shut down after the update is migrated. Give--role maintenance to shut down after completing the task
dart bin/main. dart --apply-migrations --role maintenance
To stop a Docker container at some point, just run
docker compose stopor use the Docker desktop app.
⚡️ Serverpod basic example#
Server Endpoint#
- Each Endpoint is named with Endpoint as the suffix, and the generated interface will automatically omit the suffix. and must inherit the Endpoint
- Each API method must return a Future type to execute asynchronously
- The first parameter must be
Session. Saves information about ongoing requests, which contains information about the current communication and some auxiliary APIs that provide access to databases, caches, and other functions
import 'package:serverpod/serverpod.dart';
class ExampleEndpoint extends Endpoint {
Future<String> hello(Session session, String name) async {
return 'Hello $name';
}
}
Flutter Client#
var client = Client('http://$localhost:8080/')
..connectivityMonitor = FlutterConnectivityMonitor();
var result = await client.example.hello('World');
⚡️ Example: Todo App#
Server Setup#
Model: Task
class: Task
table: task
fields:
name: String
isDone: bool
createAt: DateTime
Endpoint: Todo
class TodoEndpoint extends Endpoint {
Future<Task> createTask(Session session, Task task) async {
final createdTasks = await Task.db.insert(session, [task]);
return createdTasks.first;
}
Future<List<Task>> getTasks(Session session) async {
return await Task.db.find(session);
}
Future<Task> updateTask(Session session, Task task) async {
await Task.db.update(session, [task]);
return task;
}
Future<void> deleteTask(Session session, Task task) async {
await Task.db.delete(session, [task]);
}
}
Execute the serverpod generate command to generate Server and Client code.
Start the Server program and run the local Api Server, Database, and Redis.
cd example_server/
docker compose up --build --detach
dart bin/main.dart --apply-migrations
Client Implementation#
When using the back-end API, there is no need to process data parsing or serialization operations, achieving a seamless experience.
To connect to the server, the local test default is http://$localhost:8080/. You can replace it according to your environment.
var client = Client('http://$localhost:8080/')
..connectivityMonitor = FlutterConnectivityMonitor();
In Flutter development, we always communicate with the Server through the client object and use it to access databases and tables to do anything. Because the Server created the TodoEndpoint, after executing serverpod generate, we can use client.todo to perform operations related to the Todo table.
📍Get tasks
void getTasks() async {
final result = await client.todo.getTasks();
setState(() {
tasks.addAll(result);
});
}
📍Create task
void createTask() async {
final taskName = _textEditingController.text;
final task = Task(
name: taskName,
isDone: false,
createAt: DateTime.now(),
);
try {
final createdTask = await client.todo.createTask(task);
setState(() {
tasks.insert(0, createdTask);
_textEditingController.clear();
});
} catch (e) {
if (!mounted) {
return;
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(e.toString())));
}
}
📍Update task
Future<void> updateTask(int index) async {
final task = tasks.elementAt(index);
final newTask = task.copyWith(isDone: !task.isDone);
try {
setState(() {
tasks[index] = newTask;
});
await client.todo.updateTask(newTask);
} catch (e) {
setState(() {
tasks[index] = task;
});
if (!mounted) {
return;
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(e.toString())));
}
}
📍Delete Task
void deleteTask(int index) async {
final task = tasks.elementAt(index);
try {
setState(() {
tasks.remove(task);
});
await client.todo.deleteTask(task);
} catch (e) {
setState(() {
tasks.insert(index, task);
});
if (!mounted) {
return;
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(e.toString())));
}
}
Demo#
Execute the server and Flutter App, perform Todo CRUD operations, and check whether the database is updated normally.
Whenever a request operation is performed, the Server will have real-time Log information:
You can use Postico software to ensure that the data is reflected in the database: View the development.yaml and passwords.yaml files from the config/ directory of the project, which contains information related to the database connection.
That’s it, we have completed building a full-end Todo application using Serverpod and Flutter.
🧰 Serverpod Mini#
The v2.0 Dreamscape version has been released, a simplified lightweight version. It’s suitable to use if there are no repositories or Docker requirements and can be easily deployed to Google Cloud Run.
Suitable situation:
- No Postgre SQL database required
- Do not use Postgre SQL, use other databases
- No need for Docker
serverpod create --mini <project-name>
At this point, you can start simple back-end development!
If you want to upgrade to the full version of Serverpod due to demand, it is also very simple. If you need to use DB related functions, first switch to the server/ directory and use the following instructions:
serverpod create .
After success, you will see the config directory, deploy directory, and Docker-related files in the project.
Before upgrading, it is best to back up relevant files, as files originally created manually may be overwritten.
⚡️ Serverpod Mini Example: Todo App#
Server Implementation#
Model: Task
class: Task
fields:
id: UuidValue
name: String
isDone: bool
createAt: DateTime
Endpoint: Todo
Serverpod Mini does not have database support. It stores data temporarily in internal memory and does not need to be saved permanently.
final tasks = <Task>[];
class TodoEndpoint extends Endpoint {
Future<Task> createTask(Session session, Task task) async {
tasks.add(task);
return task;
}
Future<List<Task>> getTasks(Session session) async {
return tasks;
}
Future<Task> updateTask(Session session, Task task) async {
tasks[tasks.indexWhere((t) => t.id == task.id)] = task;
return task;
}
Future<void> deleteTask(Session session, UuidValue id) async {
tasks.removeWhere((t) => t.id == id);
}
}
Execute the serverpod generate command to generate Server and Client code.
Finally, use the CLI to start the Server program and run it on the local 8080 Port
cd example_server/
dart bin/main. dart --apply-migrations
Client Implementation#
📍Get tasks
void getTasks() async {
tasks.clear();
final result = await client.todo.getTasks();
setState(() {
tasks.addAll(result);
});
}
📍New task
void createTask() async {
final taskName = _textEditingController.text;
final task = Task(
id: UuidValue.fromString(const Uuid().v6()),
name: taskName,
isDone: false,
createAt: DateTime.now(),
);
try {
setState(() {
tasks.insert(0, task);
_textEditingController.clear();
});
final _ = await client.todo.createTask(task);
} catch (e) {
setState(() {
tasks.remove(task);
});
if (!mounted) {
return;
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(e.toString())));
}
}
📍Update task
Future<void> updateTask(int index) async {
final task = tasks.elementAt(index);
final newTask = task.copyWith(isDone: !task.isDone);
try {
setState(() {
tasks[index] = newTask;
});
await client.todo.updateTask(newTask);
} catch (e) {
setState(() {
tasks[index] = task;
});
if (!mounted) {
return;
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(e.toString())));
}
}
📍Delete task
void deleteTask(int index) async {
final task = tasks.elementAt(index);
try {
setState(() {
tasks.remove(task);
});
await client.todo.deleteTask(task.id);
} catch (e) {
setState(() {
tasks.insert(index, task);
});
if (!mounted) {
return;
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(e.toString())));
}
}
Demo#
The actual effect is the same as the previous example, the only difference is whether the data is stored in the database or the memory.
⚡️ Serverpod Mini Example: Todo App by Stream#
The implementation uses Stream and message channel reception. In fact, the concepts are similar. A sample Github link is provided at the end.
Q & A#
Is Serverpod free?#
Yes, and it’s open source. You can fork and optimize it at will, and deploy it to your ideal cloud platform (for example: AWS). At the same time, the Serverpod Cloud provided by Serverpod allows users to deploy it directly, and this part will be charged for use.
How is it different from Firebase?#
- Using Firebase requires writing all back-end logic in the front-end application, which cannot be effectively separated. Serverpod can ensure that the front and back ends are clean and handle logic independently, requiring only API calls.
- As time and scale expand, Firebase’s costs increase rapidly, and it is difficult to maintain a clean architecture. Serverpod is much more convenient than Firebase in terms of flexibility and future expansion, and it is easy to integrate other services
- Serverpod can perform real-time access, but you can choose whether to use the database. The functions are all separate and independent.
- Serverpod can be hosted on any cloud platform, AWS, GCP, etc. Even using Serverpod Cloud allows direct deployment to the Serverpod Cloud with a simple configuration and a few lines of instructions
Future releases and roadmap#
💡 Inherited Models#
Regarding the return value of the API, it can be given to Sub Class. Or when using Stream for real-time communication, you can avoid using dynamic data, and you can use inheritance classes to give strictly strong typed return values.
💡 Web Server#
Ability to deploy websites and provide web pages for users to browse.
Conclusion#
As Flutter’s exclusive backend solution, Serverpod brings unity and convenience, allowing developers to focus on the implementation of application logic rather than cumbersome backend infrastructure. Such a framework enables Flutter developers to not only create an excellent front-end experience but also realize a complete product ecosystem with the Dart backend.
GitHub#
Study and Reference#
- https://www.youtube.com/watch?v=FwttjcKyWFk&ab_channel=Serverpod%F0%9F%9A%80
- https://www.youtube.com/watch?v=zZCqpm4ZNbQ&ab_channel=HeyFlutter%E2%80%A4com
- https://www.youtube.com/watch?v=F7WKovEFdnw&ab_channel=Serverpod%F0%9F%9A%80
- https://medium.com/serverpod/serverpod-2-2-futurism-new-test-tools-and-a-sneak-peek-of-the-future-cab4d8bfdc20
- https://medium.com/serverpod/getting-started-with-serverpod-mini-58c71ffefa54
- https://suragch.medium.com/web-server-frameworks-for-dart-197a073299eb
- https://dinkomarinac.dev/dart-on-the-server-exploring-server-side-dart-technologies-in-2024
Other Articles#
- 2025 最新 Flutter 書籍《Flutter勇者之書》
- Flutter December 2024 💙 Flutter Monthly
- Flutter November 2024 💙 Flutter Monthly
- Flutter October 2024 💙 Flutter Monthly
- Flutter September 2024 💙 Flutter Monthly
- Flutter August 2024 💙 Flutter Monthly
- Flutter July 2024 💙 Flutter Monthly
- Flutter 實作 DeepLink 完整指南 ⎮ Part 4: 適配與掌握社交平台
- Flutter 實作 DeepLink 完整指南 ⎮ Part 3: Flutter 開發
- Flutter 實作 DeepLink 完整指南 ⎮ Part 2: Android 與 iOS 設定
- Flutter 實作 DeepLink 完整指南 ⎮ Part 1: 基本介紹
- Flutter June 2024 💙 Flutter Monthly
- What can I do with “Extension Types” in Dart?
- Flutter May 2024 💙 Flutter Monthly
- Flutter April 2024 💙 Flutter Monthly
- How to implement Shorebird Code Push in Flutter?
- Flutter March 2024 💙 Flutter Monthly
- Flutter February 2024 💙 Flutter Monthly
- Flutter January 2024 💙 Flutter Monthly
- Use Dart 3 to Improve Development Skills. More Examples and Tips.
- Flutter December 2023 💙 Flutter Monthly
- Flutter November 2023 💙 Flutter Monthly
- Get Familiar with Dart 3, Make your Life Easier!
本文原刊登於 Medium。