APIs with Dart Frog

Build professional RESTful APIs with Dart's minimalist framework

Download Complete Examples

What is Dart Frog?

Dart Frog is a minimalist framework for building backends in Dart. It's fast, efficient, and follows best practices for building modern RESTful APIs. Perfect for microservices and applications that need a lightweight yet powerful backend.

Fast and Lightweight

Minimalist and optimized for maximum performance

API First

Specifically designed for building RESTful APIs

Routing System

File-based structure for defining routes

File Structure

routes/
products/
shirts/
index.dart
shoes/
index.dart
[id].dart Parámetro
index.dart
users/
admin/
[id].dart
_middleware.dart Middleware
index.dart

How the routing system works:

Folders = Routes

Each folder inside routes/ becomes an API route.

Files = Endpoints

The index.dart files handle requests to that route.

[parameter].dart

Files with brackets capture dynamic parameters in the URL.

_middleware.dart

Middleware that runs before the routes in that folder.

Examples of generated routes:

GET /products → routes/products/index.dart
GET /products/123 → routes/products/[id].dart
GET /products/shirts → routes/products/shirts/index.dart
POST /users → routes/users/index.dart
GET /users/admin/456 → routes/users/admin/[id].dart

Middleware: Authentication and Validation

Middleware in Dart Frog lets you intercept and modify requests before they reach the handlers. It's ideal for:

Authentication

Validating API keys, JWT tokens, etc.

Validation

Verifying input data

Logging

Logging requests and responses

Transformation

Modifying requests/responses

Authentication Middleware:

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

Handler middleware(Handler next) {
  return (context) async {
    final apikey = context.request.headers['apikey'];
     
    if (apikey == null || apikey != '123456') {
      return Response(body: jsonEncode({'error': 'Apikey no válida'}));
    }
    
    return await next(context);
  };
}

Middleware Flow:

1
HTTP Request Arrives

Client sends a request with/without an API key

2
Middleware Intercepts

Verifies headers and API key

3
Validation

If valid → passes to the handler

If invalid → error response

Main Handler: HTTP Method Handling

The file index.dart file in each route contains the onRequest function that handles all HTTP requests to that route.

GET

Retrieve resources

Reading

POST

Create resources

Creation

PUT

Update resources

Actualización

DELETE

Delete resources

Eliminación

Handler for the root route:

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

Future<Response> onRequest(RequestContext context) async {
  switch (context.request.method) {
    case HttpMethod.get:
      return Response(body: jsonEncode({
        'método': 'GET', 
        'ruta': '/',
        'mensaje': 'Bienvenido a la API'
      }));
      
    case HttpMethod.post:
      final body = await context.request.json();
      return Response(body: jsonEncode({
        'método': 'POST',
        'datos_recibidos': body,
        'estado': 'creado'
      }));
      
    case HttpMethod.put:
      return Response(body: jsonEncode({
        'método': 'PUT',
        'mensaje': 'Recurso actualizado'
      }));
      
    case HttpMethod.delete:
      return Response(body: jsonEncode({
        'método': 'DELETE',
        'mensaje': 'Recurso eliminado'
      }));
      
    default:
      return Response(
        statusCode: 405,
        body: 'Método no permitido'
      );
  }
}

Interactive API Tester

JSON válido

API Response

Status: 200 OK Tiempo: 0ms
{
  "status": "esperando solicitud..."
}
Content-Type: application/json
Server: Dart Frog
// El código Dart se generará aquí// basado en tu solicitud
# El comando cURL se generará aquí

Complete Example: API expression in Products

Complete CRUD for Products:

GET /products Listar todos los productos
GET /products/{id} Obtener un producto específico
POST /products Crear un nuevo producto
PUT /products/{id} Actualizar un producto
DELETE /products/{id} Eliminar un producto

Handler Code:

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

Future<Response> onRequest(RequestContext context) async {
  // Obtener parámetros de ruta
  final id = context.params['id'];
  
  switch (context.request.method) {
    case HttpMethod.get:
      if (id != null) {
        // GET /products/123
        return Response(body: jsonEncode({
          'id': id,
          'nombre': 'Producto ' + id,
          'precio': 99.99
        }));
      } else {
        // GET /products
        return Response(body: jsonEncode([
          {'id': 1, 'nombre': 'Camisa', 'precio': 29.99},
          {'id': 2, 'nombre': 'Pantalón', 'precio': 49.99}
        ]));
      }
      
    case HttpMethod.post:
      final body = await context.request.json();
      return Response(
        statusCode: 201,
        body: jsonEncode({
          'id': 3,
          'producto': body,
          'estado': 'creado'
        })
      );
      
    case HttpMethod.put:
      final body = await context.request.json();
      return Response(body: jsonEncode({
        'id': id,
        'producto': body,
        'estado': 'actualizado'
      }));
      
    case HttpMethod.delete:
      return Response(body: jsonEncode({
        'id': id,
        'estado': 'eliminado'
      }));
      
    default:
      return Response(statusCode: 405);
  }
}

Parameters and Queries

Route Parameters

Capture values directly from the URL

/products/[id].dart context.params['id']

Query Parameters

Optional values after the ? in the URL

/products?categoria=shirts&orden=precio context.request.uri.queryParameters

Request Body

Data sent in POST/PUT

{ "nombre": "Producto", "precio": 100 } await context.request.json()

Handling all parameter types:

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

Future onRequest(RequestContext context) async {
  // Parámetro de ruta  final productId = context.params['id'];
  
  // Parámetros de consulta  final queryParams = context.request.uri.queryParameters;
  final categoria = queryParams['categoria'];
  final orden = queryParams['orden'] ?? 'nombre';
  
  // Cuerpo de la solicitud (para POST/PUT)  Map? body;
  if (context.request.method == HttpMethod.post || 
      context.request.method == HttpMethod.put) {
    body = await context.request.json();
  }
  
  // Headers
  final authHeader = context.request.headers['authorization'];
  
  return Response(body: jsonEncode({
    'id': productId,
    'categoria': categoria,
    'orden': orden,
    'body': body,
    'autenticado': authHeader != null
  }));
}

Best Practices

Input Validation

Always validate and sanitize the received data before processing it.

Error Handling

Use appropriate HTTP status codes and clear error messages.

Documentation

Document your API with OpenAPI/Swagger to make it easier to use.

Separation of Concerns

Separate business logic, data access and handlers.

Exercises to Practice

Exercise 1: API for a Blog

Create an API for a blog with posts, comments and authors.

Requirements:

  • GET /posts - List all posts
  • GET /posts/{id} - Get a specific post
  • POST /posts - Create a new post (with authentication middleware)
  • GET /posts/{id}/comments - Comments on a post

Exercise 2: Shopping Cart

Implement a shopping cart with products and users.

Requisitos:

  • Middleware to identify users by token
  • GET /cart - View the user's cart
  • POST /cart/items - Add product to the cart
  • DELETE /cart/items/{id} - Remove product
  • POST /cart/checkout - Complete purchase

Exercise 3: API with Advanced Search

Create an API with filters, sorting and pagination.

Requisitos:

  • GET /products?categoria=ropa&minPrecio=10&maxPrecio=100
  • Parameters: page, limit, sort, order
  • Response with metadata: total, page, totalPages
  • Cache with ETag and Last-Modified headers

Additional Resources

Official Documentation

Dart Frog Documentation

GitHub Repository

VeryGoodOpenSource/dart_frog

Video Tutorials

YouTube Tutorials

Complete Examples

Official examples