Dart Frog Logo

ci style: very good analysis License: MIT Powered by Mason

一个快速、极简的 Dart 后端框架?

Very Good Ventures ? 开发 ?

实验性 ?

Dart Frog 是一个正在开发中的实验性项目,目前不应在生产环境中使用。

快速开始 ?

先决条件 ?

为了使用 Dart Frog,您必须在您的机器上安装 Dart SDK

安装 ?‍?

# ? Install the dart_frog cli from source
dart pub global activate dart_frog_cli

创建项目 ✨

使用 dart_frog create 命令创建新项目。

# ? Create a new project called "my_project"
dart_frog create my_project

启动开发服务器 ?

接下来,打开新创建的项目并通过以下方式启动开发服务器:

# ? Start the dev server
dart_frog dev

创建生产构建 ?

创建生产构建,其中包含一个 DockerFile,以便您可以部署到任何地方

# ? Create a production build
dart_frog build

目标 ?

Dart Frog 构建在 shelfmason 之上,并受到 remix.runnext.jsexpress.js 等许多工具的启发。

Dart Frog 的目标是帮助开发人员有效地在 Dart 中构建后端。目前,Dart Frog 专注于优化聚合、组合和规范化来自多个数据源的数据的后端构建过程。Dart Frog 提供了一个具有小型 API 表面的简单核心,以降低开发人员的学习曲线和上手时间。此外,Dart Frog 旨在通过拥有一个统一的技术栈,使 Flutter/Dart 开发人员能够最大化他们的生产力,从而共享工具、模型等!

功能集 ✨

✅ 热重载 ⚡️

✅ Dart Dev Tools ⚙️

✅ 文件系统路由 ?

✅ 索引路由 ?

✅ 嵌套路由 ?

✅ 动态路由 ?

✅ 中间件 ?

✅ 依赖注入 ?

✅ 生产构建 ?‍♂️

✅ Docker ?

? 生成的 Dart 客户端包 ?

? 生成的 API 文档 ?

文档 ?

路由 ?

在 Dart Frog 中,路由由 `routes` 目录中 `*.dart` 文件导出的 `onRequest` 函数(称为路由处理程序)组成。每个端点根据其文件名与路由文件关联。名为 `index.dart` 的文件将对应于 `/` 端点。

例如,如果您创建 `routes/hello.dart` 并像下面一样导出 `onRequest` 方法,它将可以在 `/hello` 访问。

import 'package:dart_frog/dart_frog.dart';

Response onRequest(RequestContext context) {
  return Response(body: 'Hello World');
}

所有路由处理程序都可以访问 `RequestContext`,它可用于访问传入的请求以及请求上下文中提供的依赖项(请参阅中间件)。

import 'package:dart_frog/dart_frog.dart';

Response onRequest(RequestContext context) {
  // Access the incoming request.
  final request = context.request;

  // Return a response.
  return Response(body: 'Hello World');
}

路由处理程序可以是同步的或异步的。要将上面的路由处理程序转换为异步,我们只需将返回类型从 `Response` 更新为 `Future<Response>`。我们可以添加 `async` 关键字以在返回 `Response` 之前在我们的处理程序中 `await` futures。

import 'package:dart_frog/dart_frog.dart';

Future<Response> onRequest(RequestContext context) async {
  final result = await _someFuture();
  return Response(body: 'Result is: $result!');
}

动态路由 ?

Dart Frog 支持动态路由。例如,如果您创建一个名为 `routes/posts/.dart` 的文件,它将可以在 `/posts/1`、`/posts/2` 等处访问。

路由参数会像下面这样传递给 `onRequest` 方法。

import 'package:dart_frog/dart_frog.dart';

Response onRequest(RequestContext context, String id) {
  return Response(body: 'post id: $id');
}

中间件 ?

Dart Frog 中的中间件允许您在请求处理之前和之后执行代码。您可以修改传入的请求和传出的响应,提供依赖项等等!

在 Dart Frog 中,中间件是由 `routes` 文件夹子目录中的 `_middleware.dart` 文件导出的 `middleware` 函数。每个路由目录最多只能有一个中间件,其中 `routes/_middleware.dart` 是为所有传入请求执行的中间件。

import 'package:dart_frog/dart_frog.dart';

Handler middleware(Handler handler) {
  return (context) async {
    // Execute code before request is handled.

    // Forward the request to the respective handler.
    final response = await handler(context);

    // Execute code after request is handled.

    // Return a response.
    return response;
  };
}

我们可以通过 `use` API 链接内置中间件,例如 `requestLogger` 中间件。例如,如果我们创建 `routes/_middleware.dart` 并包含以下内容,我们将自动记录我们服务器的所有请求。

import 'package:dart_frog/dart_frog.dart';

Handler middleware(Handler handler) {
  return handler.use(requestLogger());
}

依赖注入 ?

中间件还可以通过 `provider` 为 `RequestContext` 提供依赖项。

`provider` 是一种中间件,可以创建并向请求上下文提供类型为 `T` 的实例。`create` 回调会被惰性调用,并且注入的 `RequestContext` 可用于执行其他查找,以访问上游提供的较高值。

在下面的示例中,我们将使用 `provider` 将 `String` 注入到我们的请求上下文中。

import 'package:dart_frog/dart_frog.dart';

Handler middleware(Handler handler) {
  return handler
      .use(requestLogger())
      .use(provider<String>((context) => 'Welcome to Dart Frog!'));
}

之后,我们可以使用 `context.read<T>()` 从路由处理程序内部访问提供的。

import 'package:dart_frog/dart_frog.dart';

Response onRequest(RequestContext context) {
  final greeting = context.read<String>();
  return Response(body: greeting);
}

测试 ?

在 Dart Frog 中,我们可以有效地单元测试我们的路由处理程序和中间件,因为它们是纯函数。

例如,我们可以使用 `package:test` 来测试上面的路由处理程序

import 'dart:io';

import 'package:dart_frog/dart_frog.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

import '../../routes/index.dart' as route;

class _MockRequestContext extends Mock implements RequestContext {}

void main() {
  group('GET /', () {
    test('responds with a 200 and greeting.', () async {
      const greeting = 'Hello World!';
      final context = _MockRequestContext();
      when(() => context.read<String>()).thenReturn(greeting);
      final response = route.onRequest(context);
      expect(response.statusCode, equals(HttpStatus.ok));
      expect(response.body(), completion(equals(greeting)));
    });
  });
}

在上面的测试中,我们使用 `package:mocktail` 来创建一个模拟的 `RequestContext`,并在调用 `context.read<String>()` 时模拟返回值。然后,我们只需要用模拟的上下文调用 `onRequest`,就可以断言响应是我们期望的。在本例中,我们正在检查 `statusCode` 和 `response body`,以确保响应是 200 状态码,并带有提供的问候语。

有关更多信息,请参阅 示例 和我们的 路线图

GitHub

查看 Github