# Dart on the server with Dart Frog

> A fast, minimalistic backend framework for Dart 🎯

- Source: https://verygood.ventures/blog/dart-frog/
- Published: 2022-05-24
- Author: Felix Angelov
- Tags: Backend, Open Source

---

We're very excited to announce that we've open sourced [Dart Frog](https://pub.dev/packages/dart_frog), an **experimental**, minimalistic backend framework for Dart 🎉

## What is Dart Frog?

Our goal is to help developers effectively build backends in Dart. Dart Frog is intended to help Flutter and Dart developers maximize their productivity by having a unified tech stack that enables sharing tooling, models, and more!

Dart Frog provides a simple core with a small API surface area in order to reduce the learning curve and ramp-up time for developers. It optimizes the process of building backends which aggregate, compose, and normalize data from multiple sources.

Dart Frog is built on top of [shelf](https://pub.dev/packages/shelf) and [mason](https://pub.dev/packages/mason) and is inspired by many tools including [remix.run](https://remix.run/), [next.js](https://nextjs.org/), and [express.js](https://expressjs.com/).

## Getting Started 🚀

The easiest way to get started is by installing the [**dart_frog_cli**](https://pub.dev/packages/dart_frog_cli) via [pub.dev](https://pub.dev/).

### Installation 💻

```shell
# Install from pub.dev
dart pub global activate dart_frog_cli
```

At this point, **dart\_frog** should be available as a command. You can verify by running **dart\_frog** in your terminal.

```shell
A fast, minimalistic backend framework for Dart.

Usage: dart_frog <command> [arguments]

Global options:
-h, --help       Print this usage information.
    --version    Print the current version.

Available commands:
  build    Create a production build.
  create   Creates a new Dart Frog app.
  dev      Run a local development server.

Run "dart_frog help <command>" for more information about a command.
```

### Create a Project 📦

Use the **dart\_frog** create command to generate a brand new project.

```shell
# 🚀 Create a new project called "my_project"
dart_frog create my_project
```

### Start the Dev Server 🏁

Next, we can open the newly created project and start the dev server via:

```shell
# 🏁 Start the dev server
dart_frog dev
```

This will start the server on [localhost:8080](http://localhost:8080/) with hot-reload enabled ⚡️

## Creating a new Route 🚏

In Dart Frog, a route consists of an **onRequest** function (called a route handler) exported from a dart file in the **routes** directory. Each endpoint is associated with a routes file based on its filename. Files named, **index.dart** will correspond to a **/** endpoint.

For example, if you create **routes/hello.dart** that exports an **onRequest** method like below, it will be accessible via the **/hello** endpoint.

```dart
import 'package:dart_frog/dart_frog.dart';

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

In addition, all route handlers have access to a **RequestContext** which can be used to access the incoming request as well as dependencies provided to the request context (see middleware below).

```dart
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');
}
```

We can customize the status code of the response via the **statusCode** parameter on the **Response** object:

```dart
import 'package:dart_frog/dart_frog.dart';

Response onRequest(RequestContext context) {
  return Response(statusCode: 204);
}
```

In addition, we can return JSON via the **Response.json** constructor:

```dart
import 'package:dart_frog/dart_frog.dart';

Response onRequest(RequestContext context) {
  return Response.json(
    body: {'hello': 'world!'},
  );
}
```

Route handlers can be synchronous or asynchronous. To convert the above route handlers to async, we just need to update the return type from **Response** to **Future<Response>**. We can also add the **async** keyword in order to **await** futures within our handler before returning a **Response**.

```dart
import 'package:dart_frog/dart_frog.dart';

Future onRequest(RequestContext context) async {
  final result = await _someFuture();
  return Response.json(body: result);
}
```

#### Dynamic Routes 🌓

Dart Frog supports dynamic routes. For example, if you create a file called **routes/posts/\[id\].dart**, then it will be accessible at endpoints: **/posts/1**, **/posts/2**, etc.

Routing parameters are forwarded to the **onRequest** method as seen below.

```dart
import 'package:dart_frog/dart_frog.dart';

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

### Middleware 🍔

Middleware in Dart Frog allows you to execute code before and after a request is processed. You can modify the inbound request and outbound responses, provide dependencies, and more! This can be useful for validating authorization, adding common headers, logging, etc.

In Dart Frog, a piece of middleware consists of a **middleware** function exported from a **\_middleware.dart** file within a subdirectory of the **routes** folder. There can only ever be one piece of middleware per route directory with **routes/\_middleware.dart** being middleware that is executed for all inbound requests.

```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;
  };
}
```

We can chain built-in middleware, such as the **requestLogger** middleware via the **use** API. For example, if we create **routes/\_middleware.dart** with the following contents, we will automatically log all requests to our server.

```dart
import 'package:dart_frog/dart_frog.dart';

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

#### Dependency Injection 💉

Middleware can also be used to provide dependencies to a **RequestContext** via a provider.

**provider** is a type of middleware that can create and provide an instance of type **T** to the request context. The create callback is called lazily and the injected **RequestContext** can be used to perform additional lookups to access values provided upstream.

In the following example, we'll use a **provider** to inject a **String** into our request context.

```dart
import 'package:dart_frog/dart_frog.dart';

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

We can later access the provided via from within a route handler using **context.read<T>()**:

```dart
import 'package:dart_frog/dart_frog.dart';

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

### Testing 🧪

In Dart Frog, we can unit test our route handlers and middleware effectively because they are plain functions.

For example, we can test our route handler above using **package:test**:

```dart
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()).thenReturn(greeting);
      final response = route.onRequest(context);
      expect(response.statusCode, equals(HttpStatus.ok));
      expect(response.body(), completion(equals(greeting)));
    });
  });
}
```

In the above test, we're using [**package:mocktail**](https://pub.dev/packages/mocktail) to create a mock **RequestContext** and stub the return value when calling **context.read<String>()**. Then, all we need to do is call **onRequest** with the mocked context and we can assert that the response is what we expect. In this case, we're checking the **statusCode** and response body to ensure that the response is a 200 with the provided greeting.

## What's Next

Check out the [Dart Frog Roadmap](https://dart-frog.dev/roadmap/) to see where we're headed.

If you're as excited about Dart Frog as we are, please let us know! We'd love to hear any feedback from the community to help shape our direction and look forward to seeing Dart becoming more prevalent on the backend ☁️
