はじめに
この記事はどすこい塾 Advent Calendar 2025 9日目の記事です。
昨日は @uhooi の テストの関数名を日本語にしよう!(Swift)でした。
こんにちは。どすこいです。
早速ですが皆さんはDart好きですか?
個人的にDartはいい言語だな〜と思うようになってきて、せっかくならFlutterもあるしフルスタックDartに挑戦してみたいなという気持ちが出てきました。
現状サーバーサイドDartのフレームワークがいくつかありますが(後述)、好奇心からせっかくなら自分で作ってしまえと思い開発を始めました。
現在開発をしているDart製Webフレームワーク「Aim」の紹介をしていきたいと思います。
サーバーサイドDartの魅力
まず、なぜサーバーサイドでDartを使うのか?という話から始めましょう。
JITとAOTの良いとこ取り
Dartの最大の魅力は、開発時と本番環境で異なるコンパイル方式を使い分けられることです
- 開発時(JIT)
- ホットリロードが使える
- コードの変更がすぐに反映される
- デバッグが容易
- 本番環境(AOT)
- コールドスタートが非常に速い
- バンドルサイズが小さい
- メモリ使用量が少ない
- 実行速度が速い
- ネイティブ実行なので環境を選ばない
つまり、開発時は快適に開発できて、本番では高速かつ軽量に動作するという、良いとこ取りができます。
型安全性
null安全に加え、Record Typeやsealed classなどモダンな機能が備わっています。
既存のDartフレームワーク
サーバーサイドDartには、すでにいくつかのフレームワークが存在します。それぞれの特徴と、私が感じた「足りないもの」を整理してみます。
shelf
特徴
- Dartの標準的なHTTPサーバーライブラリ
- シンプルで低レベル
- 柔軟性が高い
足りないと感じた点
- ボイラープレートが多い
- ルーティングやミドルウェアの構成に手間がかかる
- 開発者体験を向上させる機能(ホットリロードなど)が標準で提供されていない
serverpod
特徴
- フルスタックフレームワーク
- ORM、認証、リアルタイム通信など全部入り
- 大規模アプリケーション向け
足りないと感じた点
- 学習曲線が急
- シンプルなAPIサーバーを作りたいだけなのに、機能が多すぎる
- セットアップが複雑
dart_frog
特徴
- Fast Refresh対応
- ファイルベースルーティング
- VercelのEdge Functionsに似た設計
足りないと感じた点
- ミドルウェアの合成がやや複雑
- あまり直感的ではない(個人的に)
なぜAimを作ろうとしたか
上記のフレームワークを使っていて、僕が求めていたのは以下のようなものでした
- Honoライクな直感的で関数ベースのAPI
- ホットリロード、CLIツール、テストユーティリティなどのより良い開発者体験
- 軽量でありながら、必要な機能をプラグイン的に導入できるモジューラ設計
- Dartの型システムを最大限活用
Aimの設計思想
HonoライクなAPI
Aimは、TypeScript界で人気のHonoフレームワークに強く影響を受けています。シンプルで直感的なContext APIを採用しています。
import 'package:aim_server/aim_server.dart';
void main() async {
final app = Aim();
// ミドルウェア
app.use((c, next) async {
print('${c.method} ${c.path}');
await next();
});
// ルーティング
app.get('/', (c) async {
return c.json({'message': 'Hello, Aim!'});
});
app.get('/users/:id', (c) async {
final id = c.param('id');
return c.json({'userId': id});
});
app.post('/api/users', (c) async {
final data = await c.req.json();
return c.json({'message': 'User created', 'data': data});
});
await app.serve(port: 8080);
}
モジュラーアーキテクチャ
Aimは、機能ごとに分離されたパッケージ構成を採用しています。必要な機能だけをインストールできるので、アプリケーションを軽量に保てるように設計しています。
- aim_server
- ルーティング、ミドルウェア、リクエスト/レスポンス処理などのコア機能
- aim_cli
- CLIツール(プロジェクト生成、ホットリロード)
- aim_server_cors
- CORS対応
- aim_server_cookie
- Cookie管理
- aim_server_static
- 静的ファイル配信
- aim_server_multipart
- マルチパートデータ処理
- aim_server_form
- フォームデータ処理
- aim_server_testing
- テストユーティリティ
CLIツール
CLIツールにはいくつかの機能が搭載されています。
ホットリロード
開発者体験を向上させるため、Aimにはファイル監視ベースのホットリロード機能が組み込まれています。
aim dev
これだけで、ファイルの変更を検知して自動的にサーバーを再起動してくれます。libやbinディレクトリを監視し、変更があれば即座に反映されるので、開発がとても快適です。
プロジェクト作成
aim create <project_name>
上記のコマンドでテンプレートプロジェクトが作成されます。Dockerfileも含まれており、後述するビルドコマンドを使うと、簡単にネイティブバイナリを作成でき、どこでも動くWebアプリケーションが作成できます。
ビルド
aim build
ネイティブバイナリを作成するためのコマンドです。
環境変数対応
pubspec.yamlにaimセクションを作ることで、aim devで動かした際のアプリケーションに環境変数を渡すことができます。
name: ~~
environment:
sdk: ^3.10.0
dependencies:
aim_server: ^0.0.5
dev_dependencies:
test: ~~
# ここから
aim:
entry: bin/server.dart
env:
DATABASE_URL: $DATABASE_URL
PORT: ${PORT:8080}
環境変数の展開とデフォルト値の設定ができるようになっています。
最後に
僕はもともとモバイルメインで開発していたエンジニアなので、あまりWebに詳しくなく開発するためにRFCをよく読むようになりました。
技術的な好奇心から作り始めたものですが、そういった観点からも色々なことを知るきっかけになり非常に楽しく開発を進められています。
Dartはいい言語ではありますが、エコシステムの発展が他の言語に比べまだまだだと思うところもあるので、自分がそれに少しでも寄与できたらと思っています。
もし興味がある方は試していただけたらとても嬉しいです。
リンク
- GitHub: https://github.com/aim-dart/aim
- pub.dev: https://pub.dev/packages/aim_server
フィードバックやコントリビュートも大歓迎です。一緒により良いフレームワークを作っていきましょう!