Serverpod 适合全栈 Dart 开发吗?
视频
https://www.bilibili.com/video/BV1nM4m1k7DE/
前言
原文 https://ducafecat.com/blog/full-stack-development-with-dart-and-serverpod
Serverpod 是一个符合现代化需求的大前端框架,支持 Docker 部署、代码自动生成、数据库连接等功能。对于只会 Dart 语言的开发者来说,Serverpod 是否能胜任全栈开发?本文为您详细评估 Serverpod 的优势和局限性。
Serverpod, Dart 全栈开发, Dart 开发框架, 大前端框架, 全栈开发解决方案
Serverpod
主要功能有:
- Docker 生态部署
- 代码自动生成 model、dao、controller、client sdk
- 连接 postgres、redis
- 可配置 yaml , 开发、生产隔离
- 可视化日志UI工具 insights
- 缓存、日志、验证、上传文件、健康检查、调度任务、流消息、RPC
如果你只是用来做以上业务,没有问题,但是再复杂些就要靠生态了,如图片加工、视频压缩、经纬度转换、P2P等等,你估计要用 java、go 这种生态丰富的语言来搭建了,然后用 Serverpod 来访问,当然你也可以用 saas 云服务解决。
如果你正在构建一个精简的单体服务架构,你可以从 Serverpod 的设计中寻找灵感。
参考
https://github.com/serverpod/serverpod
https://docs.serverpod.dev/insights
https://marketplace.visualstudio.com/items?itemName=serverpod.serverpod
前提条件
- flutter dart 运行时环境
https://docs.flutter.dev/get-started/install - docker 运行环境
https://docs.docker.com/desktop/install/mac-install/
步骤
1 全局安装 serverpod 命令行工具
安装
$ dart pub global activate serverpod_cli
自检
$ serverpod
❯ serverpod
Manage your serverpod app development
Usage: serverpod <command> [arguments]
Global options:
-h, --help Print this usage information.
-q, --quiet Suppress all serverpod cli output. Is overridden by -v, --verbose.
-v, --verbose Prints additional information useful for development. Overrides --q, --quiet.
-a, --[no-]analytics Toggles if analytics data is sent to Serverpod.
(defaults to on)
Available commands:
create Creates a new Serverpod project, specify project name (must be lowercase with no special characters).
create-migration Creates a migration from the last migration to the current state of the database.
create-repair-migration Repairs the database by comparing the target state to what is in the live database instead of comparing to
the latest migration.
generate Generate code from yaml files for server and clients.
language-server Launches a serverpod language server communicating with JSON-RPC-2 intended to be used with a client
integrated in an IDE.
version Prints the active version of the Serverpod CLI.
Run "serverpod help <command>" for more information about a command.
2 创建项目
create 命令
$ serverpod create mypod
mypod 是项目名称
目录
代码 | 名称 |
---|---|
mypod_client | 客户端链接库 |
mypod_flutter | flutter 项目 |
mypod_server | serverpod 服务端 |
3 启动项目
Docker 方式启动 postgres、redis
$ cd mypod/mypod_server
$ docker compose up --build --detach
启动 Serverpod 服务
$ dart bin/main.dart --apply-migrations
启动 Flutter 程序
$ cd mypod/mypod_flutter
$ flutter run -d chrome
4 配置文件
开发、生产、测试 配置
mypod_server/config/development.yaml
mypod_server/config/production.yaml
mypod_server/config/staging.yaml
# This is the configuration file for your local development environment. By
# default, it runs a single server on port 8080. To set up your server, you will
# need to add the name of the database you are connecting to and the user name.
# The password for the database is stored in the config/passwords.yaml.
#
# When running your server locally, the server ports are the same as the public
# facing ports.
# Configuration for the main API server.
apiServer:
port: 8080
publicHost: localhost
publicPort: 8080
publicScheme: http
# Configuration for the Insights server.
insightsServer:
port: 8081
publicHost: localhost
publicPort: 8081
publicScheme: http
# Configuration for the web server.
webServer:
port: 8082
publicHost: localhost
publicPort: 8082
publicScheme: http
# This is the database setup for your server.
database:
host: localhost
port: 8090
name: mypod
user: postgres
# This is the setup for Redis.
redis:
enabled: false
host: localhost
port: 8091
密码配置
mypod_server/config/passwords.yaml
# Use this file to store passwords to services that your server you use. When
# the server starts, the passwords will be automatically loaded and can be
# accessed from the `session.passwords` field. If you don't have access to a
# session object, passwords can also be accessed from
# `Serverpod.instance.passwords`. You can provide different passwords for
# different run configurations. If you want the same password for any
# configuration used, place it under `shared`.
#
# Note that this file should not be under version control. Store it in a safe
# place.
# Save passwords used across all configurations here.
shared:
mySharedPassword: 'my password'
# These are passwords used when running the server locally in development mode
development:
database: 'jBXWslvjYpfszBbFKS661o1MrY1esjVC'
redis: 'ijH4ON9CCHtK9Cojnqivvym1zxHkwjig'
# The service secret is used to communicate between servers and to access the
# service protocol.
serviceSecret: 'uVoVJ-cqnwc9Bn8gQG-ZLtYfgXGnMUu3'
# Passwords used in your staging environment if you use one. The default setup
# use a password for Redis.
staging:
database: '3_5RPi7yVOSwSEhr8ecNftp1K4H0_7nL'
serviceSecret: 'JgM8DPORU5OwOyG23_gdjhYuRLD6mk1J'
# Passwords used in production mode.
production:
database: 'ReE97mCuS0QaxlXEy4hNMQYtE_Q1lxP4'
serviceSecret: '0XpMqP4aybryFxm4N8V5ZXHcPFTHUXpP'
生成配置
type: server
client_package_path: ../mypod_client
docker 服务配置
mypod_server/docker-compose.yaml
version: '3.7'
services:
postgres:
image: postgres:14.1
ports:
- '8090:5432'
environment:
POSTGRES_USER: postgres
POSTGRES_DB: mypod
POSTGRES_PASSWORD: "jBXWslvjYpfszBbFKS661o1MrY1esjVC"
volumes:
- mypod_data:/var/lib/postgresql/data
redis:
image: redis:6.2.6
ports:
- '8091:6379'
command: redis-server --requirepass "ijH4ON9CCHtK9Cojnqivvym1zxHkwjig"
environment:
- REDIS_REPLICATION_MODE=master
volumes:
mypod_data:
5 动态生成接口代码
添加 example.hello2 方法
mypod_server/lib/src/endpoints/example_endpoint.dart
class ExampleEndpoint extends Endpoint {
// You create methods in your endpoint which are accessible from the client by
// creating a public method with `Session` as its first parameter.
// `bool`, `int`, `double`, `String`, `UuidValue`, `Duration`, `DateTime`, `ByteData`,
// and other serializable classes, exceptions and enums from your from your `protocol` directory.
// The methods should return a typed future; the same types as for the parameters are
// supported. The `session` object provides access to the database, logging,
// passwords, and information about the request being made to the server.
Future<String> hello(Session session, String name) async {
return 'Hello $name';
}
Future<String> hello2(Session session, String name) async {
return 'Hello $name';
}
}
执行生成命令
$ cd mypod/mypod_server
$ serverpod generate
查看 generated 目录的文件变化
mypod_server/lib/src/generated
客户端 lib 库
mypod_client/lib/src/protocol/client.dart
class EndpointExample extends _i1.EndpointRef {
EndpointExample(_i1.EndpointCaller caller) : super(caller);
String get name => 'example';
_i2.Future<String> hello(String name) => caller.callServerEndpoint<String>(
'example',
'hello',
{
'name': name},
);
_i2.Future<String> hello2(String name) => caller.callServerEndpoint<String>(
'example',
'hello2',
{
'name': name},
);
}
多了
hello2
的定义
Flutter 项目中调用
mypod_flutter/lib/main.dart
void _callHello2() async {
try {
final result = await client.example.hello2(_textEditingController.text);
setState(() {
_errorMessage = null;
_resultMessage = result;
});
} catch (e) {
setState(() {
_errorMessage = '$e';
});
}
}
...
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: ElevatedButton(
onPressed: _callHello2,
child: const Text('Send to Server'),
),
),
6 动态生成数据库代码
编写 model 定义
mypod_server/lib/src/models/note.yaml
class: Note
table: note
fields:
text: String
生成代码
serverpod generate
编写 CURD 接口定义
mypod_server/lib/src/endpoints/notes_endpoint.dart
import 'package:serverpod/server.dart';
import '../generated/protocol.dart';
class NotesEndpoint extends Endpoint {
Future<List<Note>> getAllNotes(Session session) async {
// By ordering by the id column, we always get the notes in the same order
// and not in the order they were updated.
return await Note.db.find(
session,
orderBy: (t) => t.id,
);
}
Future<void> createNote(Session session, Note note) async {
await Note.db.insertRow(session, note);
}
Future<void> deleteNote(Session session, Note note) async {
await Note.db.deleteRow(session, note);
}
}
生成迁移内容
$ serverpod create-migration
服务器启动应用迁移
$ cd mypod/mypod_server
$ dart bin/main.dart --apply-migrations
重启服务
迁移完成后需要重新启动服务器
dart bin/main.dart --apply-migrations
Flutter 编写代码
mypod_flutter/lib/main.dart
void _callNoteCreate() async {
try {
await client.notes.createNote(Note(text: "this is example."));
setState(() {
_errorMessage = null;
_resultMessage = "create send~";
});
} catch (e) {
setState(() {
_errorMessage = '$e';
});
}
}
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: ElevatedButton(
onPressed: _callNoteCreate,
child: const Text('Note Create Send To Server'),
),
),
Flutter 项目访问测试
$ cd mypod/mypod_flutter
$ flutter run -d chrome
7 部署项目
Docker 部署
mypod_server/Dockerfile
FROM dart:3.2.5 AS build
WORKDIR /app
COPY . .
RUN dart pub get
RUN dart compile exe bin/main.dart -o bin/server
FROM alpine:latest
ENV runmode=production
ENV serverid=default
ENV logging=normal
ENV role=monolith
COPY --from=build /runtime/ /
COPY --from=build /app/bin/server server
COPY --from=build /app/config/ config/
COPY --from=build /app/web/ web/
EXPOSE 8080
EXPOSE 8081
EXPOSE 8082
ENTRYPOINT ./server --mode=$runmode --server-id=$serverid --logging=$logging --role=$role
构建 docker 包
$ cd mypod/mypod_server
$ docker build -t mypod_server:v1.0 .
原生部署
mypod_server/deploy
- aws
- gcp
代码
官方有些不错的例子
https://docs.serverpod.dev/tutorials/code-example
小结
Serverpod 是一个功能强大的 Dart 全栈开发框架,为只掌握 Dart 语言的开发者提供了一个全面的解决方案。它支持 Docker 部署、代码自动生成、数据库连接等关键功能,满足了现代化应用开发的需求。但是对于更加复杂的业务场景,开发者可能还需要使用其他语言的生态工具。总的来说,Serverpod 是一个值得 Dart 开发者认真考虑的全栈开发框架,能够大幅提高开发效率和部署效果。
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
flutter 学习路径
- Flutter 优秀插件推荐 https://flutter.ducafecat.com
- Flutter 基础篇1 - Dart 语言学习 https://ducafecat.com/course/dart-learn
- Flutter 基础篇2 - 快速上手 https://ducafecat.com/course/flutter-quickstart-learn
- Flutter 实战1 - Getx Woo 电商APP https://ducafecat.com/course/flutter-woo
- Flutter 实战2 - 上架指南 Apple Store、Google Play https://ducafecat.com/course/flutter-upload-apple-google
- Flutter 基础篇3 - 仿微信朋友圈 https://ducafecat.com/course/flutter-wechat
- Flutter 实战3 - 腾讯即时通讯 第一篇 https://ducafecat.com/course/flutter-tim
- Flutter 实战4 - 腾讯即时通讯 第二篇 https://ducafecat.com/course/flutter-tim-s2
© 猫哥
ducafecat.com
end