DEV Community

Ushieru Kokoran
Ushieru Kokoran

Posted on

Dart Frog

Requisitos:

  • Dart >=2.17.0 <3.0.0

Dart Frog 🐸 es un framework web construido sobre Shelf inspirado por Next.JS, Remix y Express.

Otro Angel o Aqueduct?

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 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 🚀 donde desplegamos en Railway 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 por si solo quieres ver el resultado final.

Definamos un modelo básico de Todo.

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

Enter fullscreen mode Exit fullscreen mode

Por el momento las rutas se crean al mas puro estilo de Nextjs... 🙅‍♂️

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

Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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

// 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);

Enter fullscreen mode Exit fullscreen mode

Echemos a volar nuestro proyecto.

$ dart_frog dev
Enter fullscreen mode Exit fullscreen mode

termianl

Y ya lo tenemos 🎉

Get Todos

Post Todos

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

Oldest comments (0)