Dart Frog
Tags: dart, webdev, spanish, programming
Published: 2022-09-10 16:49:54

Requisitos:
- Dart >=2.17.0 <3.0.0

[Dart Frog 🐸](https://github.com/VeryGoodOpenSource/dart_frog) es un framework web construido sobre [Shelf](https://github.com/dart-lang/shelf/tree/master/pkgs/shelf) inspirado por [Next.JS](https://nextjs.org/), [Remix](https://remix.run/) y [Express](https://expressjs.com/).

### Otro [Angel](https://angel-dart.dev/) o [Aqueduct](https://aqueduct.io/)?
Solo algunos que planeábamos hacer backend con Dart teníamos la esperanza con estos dos frameworks que al día de hoy lamentablemente ya no nos acompañan.
Sin embargo podemos ver que este nuevo proyecto de llevar Dart al backend (otra vez) esta en buenas manos. [Very Good Ventures](https://verygood.ventures/) esta detrás de esto por lo que podemos esperar grandes cosas, aunque tampoco dejemos de lado nuestro apoyo como comunidad, actualmente no se aceptan PR por que el roadmap esta ya bien definido, pero issues son bienvenidos.
Asi que hablando de roadmap...

### Que nos ofrecen?
✅ Hot Reload ⚡️
✅ Dart Dev Tools ⚙️
✅ File System Routing 🚏
✅ Index Routes 🗂
✅ Nested Routes 🪆
✅ Dynamic Routes 🌓
✅ Middleware 🍔
✅ Dependency Injection 💉
✅ Production Builds 👷‍♂️
✅ Docker 🐳
✅ Static File Support 📁

### Que tienen en su radar?
🚧 Dart API Client Generation
🚧 Open API Documentation Generation
🚧 DartFrog Testing Library (utilities for unit and e2e testing)
🚧 CLI new command to generate new routes and middleware
🚧 Health Check endpoint for monitoring
🚧 Logger which can be configured to adhere to standard log formats (https://cloud.google.com/run/docs/logging)
🚧 Websocket support
🚧 VSCode/IntelliJ support for DartFrog
🚧 Create a new project
🚧 New Routes
🚧 New Middleware
🚧 Attach Debugger
🚧 CLI deploy command to support deploying to supported cloud platforms (e.g: Cloud Run)

### Dart en Backend
Sin duda un proyecto prometedor en el que pongo la confianza para regresar a Dart al backend. En su momento hice un pequeño blog sobre [Dart - From scratch to deploy 🚀](https://dev.to/ushieru/dart-from-scratch-to-deploy-2o4l) donde desplegamos en [Railway](https://railway.app/) un servidor con Dart. Voy a traer un ejemplo actualizado con Dart Frog, un poco mas complejo para que valga la pena.

Pero mientras tanto que tal si hacemos un pequeñísimo ejemplo de Todo app? Que nada mas registre y liste, solo para no irnos sin programar algo. 🧑‍💻

### Empecemos

> Te dejo el [repositorio](https://github.com/ushieru/dart_frog_todo_example) por si solo quieres ver el resultado final.

Definamos un modelo básico de Todo.

```dart
// models/todo.dart

import 'package:uuid/uuid.dart';

enum Status { open, inProgress, close }

class Todo {
  Todo(String? id, this.title, this.description, this.status)
      : id = id ?? const Uuid().v4();

  factory Todo.fromJson(Map<String, dynamic> json) {
    return Todo(
      json['id'] as String,
      json['title'] as String,
      json['description'] as String,
      Status.values
          .firstWhere((status) => json['status'] as String == status.name),
    );
  }

  String id;
  String title;
  String description;
  Status status;

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'description': description,
      'status': status.name
    };
  }
}

```

Por el momento las rutas se crean al mas puro estilo de Nextjs... 🙅‍♂️
```dart
FutureOr<Response> onRequest(RequestContext context) async {
  switch (context.request.method) {
    case HttpMethod.get:
      return Response(body: 'Get');
    case HttpMethod.post:
      return Response(body: 'Post');
    case HttpMethod.delete:
    case HttpMethod.head:
    case HttpMethod.options:
    case HttpMethod.patch:
    case HttpMethod.put:
      return Response(statusCode: HttpStatus.methodNotAllowed);
  }
}

```
Y a mi no me encanta ese método así que hagamos una pequeña clase que nos ayude un poco con el boilerplate.

````dart
// utils/route_builder.dart

import 'dart:async';
import 'dart:io';

import 'package:dart_frog/dart_frog.dart';

class RouteBuilder {
  final Map<HttpMethod, Handler> _handlers = {};

  FutureOr<Response> hanlder(RequestContext context) {
    if (_handlers.containsKey(context.request.method)) {
      return _handlers[context.request.method]!(context);
    }
    return Response(statusCode: HttpStatus.methodNotAllowed);
  }

  void get(Handler handler) => _handlers[HttpMethod.get] = handler;

  void post(Handler handler) => _handlers[HttpMethod.post] = handler;

  void delete(Handler handler) => _handlers[HttpMethod.delete] = handler;

  void head(Handler handler) => _handlers[HttpMethod.head] = handler;

  void options(Handler handler) => _handlers[HttpMethod.options] = handler;

  void patch(Handler handler) => _handlers[HttpMethod.patch] = handler;

  void put(Handler handler) => _handlers[HttpMethod.put] = handler;
}

```

Y con todo listo solo creemos nuestra nueva ruta para **todos**.

```dart
// routes/todos.dart

import 'dart:async';

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

import '../models/todo.dart';
import '../utils/route_builder.dart';

final _todos = [];

final _routeBuilder = RouteBuilder()
  ..get((context) {
    return Response.json(body: _todos);
  })
  ..post((context) async {
    final jsonTodo = await context.request.json();
    jsonTodo['status'] = 'open';
    jsonTodo['id'] = const Uuid().v4();

    final newTodo = Todo.fromJson(jsonTodo);
    _todos.add(newTodo);

    return Response.json(body: newTodo.toJson());
  });

FutureOr<Response> onRequest(RequestContext context) async =>
    _routeBuilder.hanlder(context);

```

Echemos a volar nuestro proyecto.
````sh
$ dart_frog dev
```

![termianl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnf1rk18gxssoexqczpe.png)

### Y ya lo tenemos 🎉

![Get Todos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0ktg6uatiwifqs2xspn.png)

![Post Todos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i1u2ddkfjlmc0n6braz4.png)

Integramos react? Hacemos de Dart Frog FullStack?
Cuéntame en los comentarios y Happy Hacking 🧑‍💻🎉