DartでWEBサーバー

4 min read読了の目安(約3900字

最近Flutterに触れており、せっかくなのでDartで得意分野のサーバーサイドを作ろうと思い立ったので調べながら手を動かしながら本記事を書きます
公式とstackoverflowの二つを頼りに進めるので誤っている点もあるかもしれません
誤った内容やより良い方法があれば指摘をコメントください
※参考サイトや根拠となるサイトを併記してコメントください

パッケージの選択肢

検索したところ下記の4つが見つかったが上二つはお陀仏しているので簡単そうなjaguarを試す事に決定
💀aqueduct
💀angel
shelf
jaguar

※結局shelfも試します

jaguarで作る

まずはReadmeとExampleとInstallingの説明を頼りに作る

プロジェクト作成

dart create server_jaguar
cd server_jaguar
dart run

Hello world!

パッケージの追加

pubspec.yaml
dependencies:
  jaguar: ^3.0.11

エントリーポイントの修正

bin/server_jaguar.dart
import 'package:jaguar/jaguar.dart';

main() => Jaguar(port: 10000)..get('/', (ctx) => 'Hello world!')..serve();

サーバー起動

dart run

localhost:10000

動いた

ルーティングを追加する

bin/server_jaguar.dart
//main() => Jaguar(port: 10000)..get('/', (ctx) => 'Hello world!')..serve();void main() => Jaguar(port: 10000)
..get('/', (ctx) => 'Hello world!')
..get('/gold', (ctx) => 'Hello gold!')
..serve();

localhost:10000localhost:10000/gold、どちらも動いた

公式ホームページを参考にRESTにする

https://jaguar-dart.com/
こちらを参考にControllerやJWTを試そうと思ったが必要なパッケージがnullsafetyに対応しておらずインストールできなかった
よってこの時点でjaguarは選択肢から除外

shelfで作る

pubspec.yaml
dependencies:
  shelf: ^1.1.4

エントリーポイントの修正

bin/server_jaguar.dart
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

void main() async {
  var handler = const Pipeline().addMiddleware(logRequests()).addHandler(_echoRequest);
  var server = await shelf_io.serve(handler, 'localhost', 8080);
  server.autoCompress = true;
  print('Serving at http://${server.address.host}:${server.port}');
}

Response _echoRequest(Request request) => Response.ok('Request for "${request.url}"');

nodejsライクに感じる

サーバー起動

dart run

localhost:8080
動いた

ルーティングを追加し、RESTにする

pubspec.yaml
dependencies:
  shelf: ^1.1.4
  shelf_router: ^1.0.0
bin/server_jaguar.dart
import 'dart:async' show Future;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

class Service {
  Handler get handler {
    final router = Router();

    router.get('/', (Request request) {
      return Response.ok('Get request');
    });

    router.get('/<id>', (Request request, String id) {
      return Response.ok('path parameter $id');
    });

    router.post('/', (Request request) async {
      final body = await request.readAsString();
      final params = Uri.splitQueryString(body);
      return Response.ok(params['msg']);
    });

    router.patch('/', (Request request) {
      return Response.ok('patch request');
    });

    router.put('/', (Request request) {
      return Response.ok('put request');
    });

    router.get('/delay', (Request request) async {
      await Future.delayed(Duration(milliseconds: 100));
      return Response.ok('_o/');
    });

    router.mount('/api/', Api().router);

    router.all('/<ignored|.*>', (Request request) {
      return Response.notFound('Page not found');
    });

    return router;
  }
}

class Api {
  Future<Response> _messages(Request request) async {
    return Response.ok('[]');
  }

  Router get router {
    final router = Router();
    router.get('/messages', _messages);
    router.get('/messages/', _messages);
   router.all('/<ignored|.*>', (Request request) => Response.notFound('null'));

    return router;
  }
}
void main() async {
  final service = Service();
  final server = await shelf_io.serve(service.handler, 'localhost', 8080);
  print('Server running on localhost:${server.port}');
}

curlでリクエスト飛ばしたところ動作した

コンパイル & 実行

dart compile exe .\bin\server_jaguar.dart
.\bin\server_jaguar.exe

後書き

パッケージは余計なものを試さず公式(Dartの場合Google)が提供しているものを使うに限る

https://dart.dev/tutorials/server/httpserver

本当はDBなども試したかったけどpostの処理の仕方を調べるのにも時間が掛かったので本記事はここで終わり