Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/khode-io/nest-dart/llms.txt

Use this file to discover all available pages before exploring further.

Middleware Patterns

Nest Frog provides middleware integration for Dart Frog applications, enabling dependency injection and module-based architecture in your backend routes.

The nestFrogMiddleware

The core middleware initializes the application container and makes services available to all routes:
import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';

Handler middleware(Handler handler) {
  return handler
      .use(nestFrogMiddleware(AppModule()));
}

How It Works

From packages/nest_frog/lib/src/middleware.dart:7-18:
Middleware nestFrogMiddleware(Module appModule) {
  return (handler) {
    return (context) async {
      // Initialize container if not already done
      if (!Modular.isInitialized) {
        await Modular.initialize(appModule);
      }

      return handler(context);
    };
  };
}
The middleware checks if the container is already initialized to avoid duplicate initialization on every request.

Basic Middleware Setup

In a Dart Frog project, create routes/_middleware.dart:
import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';
import 'package:your_app/app_module.dart';

Handler middleware(Handler handler) {
  return handler
      .use(nestFrogMiddleware(AppModule()));
}
This makes all services from AppModule available to your routes:
// routes/users/index.dart
import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';
import 'package:your_app/services/user_service.dart';

Future<Response> onRequest(RequestContext context) async {
  final userService = Modular.get<UserService>();
  final users = await userService.getAllUsers();
  
  return Response.json(body: users);
}

Middleware Composition

Combine nest middleware with other Dart Frog middleware:
import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';

final message = 'Welcome to Dart Frog with NestJS-like Modules!';

Handler middleware(Handler handler) {
  return handler
      .use(nestFrogMiddleware(AppModule()))
      .use(provider<String>((context) => message))
      .use(loggingMiddleware())
      .use(corsMiddleware());
}

Middleware loggingMiddleware() {
  return (handler) {
    return (context) async {
      final stopwatch = Stopwatch()..start();
      final response = await handler(context);
      print('Request processed in ${stopwatch.elapsedMilliseconds}ms');
      return response;
    };
  };
}

Middleware corsMiddleware() {
  return (handler) {
    return (context) async {
      final response = await handler(context);
      return response.copyWith(
        headers: {
          ...response.headers,
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
        },
      );
    };
  };
}
Middleware order matters! The nest middleware should typically be one of the first middleware in the chain to ensure the container is initialized before other middleware try to access services.

Context-Based Service Access

Use Modular.of(context) for context-aware service resolution:
import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';

Future<Response> onRequest(RequestContext context) async {
  // Context-based access
  final userService = Modular.of(context).get<UserService>();
  
  // Falls back to container if not in context
  final users = await userService.getAllUsers();
  
  return Response.json(body: users);
}
The ModularContext class from packages/nest_frog/lib/src/nest_frog.dart:60-88:
class ModularContext {
  final RequestContext _context;

  ModularContext._(this._context);

  /// Get a service from the request context
  T get<T extends Object>() {
    try {
      return _context.read<T>();
    } catch (e) {
      // Fallback to container if not in context
      return Modular.get<T>();
    }
  }

  /// Check if a service is available in the context
  bool has<T extends Object>() {
    try {
      _context.read<T>();
      return true;
    } catch (e) {
      return false;
    }
  }
}

Creating Custom Middleware

Authentication Middleware

import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';
import 'package:your_app/services/auth_service.dart';

Middleware authMiddleware() {
  return (handler) {
    return (context) async {
      final authService = Modular.get<AuthService>();
      final token = context.request.headers['authorization'];
      
      if (token == null) {
        return Response.json(
          statusCode: 401,
          body: {'error': 'Unauthorized'},
        );
      }
      
      final user = await authService.validateToken(token);
      if (user == null) {
        return Response.json(
          statusCode: 401,
          body: {'error': 'Invalid token'},
        );
      }
      
      // Provide user to downstream handlers
      return handler(
        context.provide<User>(() => user),
      );
    };
  };
}

Request Validation Middleware

Middleware validationMiddleware() {
  return (handler) {
    return (context) async {
      final request = context.request;
      
      // Only validate POST/PUT requests
      if (request.method != HttpMethod.post && 
          request.method != HttpMethod.put) {
        return handler(context);
      }
      
      final contentType = request.headers['content-type'];
      if (contentType != 'application/json') {
        return Response.json(
          statusCode: 400,
          body: {'error': 'Content-Type must be application/json'},
        );
      }
      
      try {
        final body = await request.json();
        return handler(
          context.provide<Map<String, dynamic>>(() => body),
        );
      } catch (e) {
        return Response.json(
          statusCode: 400,
          body: {'error': 'Invalid JSON body'},
        );
      }
    };
  };
}

Rate Limiting Middleware

import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';
import 'package:your_app/services/rate_limiter_service.dart';

Middleware rateLimitMiddleware({int maxRequests = 100, Duration window = const Duration(minutes: 1)}) {
  return (handler) {
    return (context) async {
      final rateLimiter = Modular.get<RateLimiterService>();
      final ip = context.request.headers['x-forwarded-for'] ?? 
                 context.request.headers['x-real-ip'] ?? 
                 'unknown';
      
      final isAllowed = await rateLimiter.checkLimit(
        ip,
        maxRequests: maxRequests,
        window: window,
      );
      
      if (!isAllowed) {
        return Response.json(
          statusCode: 429,
          body: {'error': 'Too many requests'},
          headers: {
            'Retry-After': window.inSeconds.toString(),
          },
        );
      }
      
      return handler(context);
    };
  };
}

Route-Specific Middleware

Apply middleware to specific route groups:
// routes/api/_middleware.dart
import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';

Handler middleware(Handler handler) {
  return handler
      .use(authMiddleware())
      .use(validationMiddleware());
}

// routes/public/_middleware.dart
import 'package:dart_frog/dart_frog.dart';

Handler middleware(Handler handler) {
  return handler
      .use(corsMiddleware())
      .use(loggingMiddleware());
}
Dart Frog applies middleware hierarchically. Middleware in parent directories applies to all child routes.

Error Handling Middleware

Middleware errorHandlingMiddleware() {
  return (handler) {
    return (context) async {
      try {
        return await handler(context);
      } on ValidationException catch (e) {
        return Response.json(
          statusCode: 400,
          body: {'error': 'Validation failed', 'details': e.errors},
        );
      } on NotFoundException catch (e) {
        return Response.json(
          statusCode: 404,
          body: {'error': e.message},
        );
      } on UnauthorizedException catch (e) {
        return Response.json(
          statusCode: 401,
          body: {'error': e.message},
        );
      } catch (e, stackTrace) {
        print('Unhandled error: $e');
        print('Stack trace: $stackTrace');
        return Response.json(
          statusCode: 500,
          body: {'error': 'Internal server error'},
        );
      }
    };
  };
}

Complete Middleware Stack Example

import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';
import 'package:your_app/app_module.dart';

Handler middleware(Handler handler) {
  return handler
      // 1. Initialize DI container (first!)
      .use(nestFrogMiddleware(AppModule()))
      // 2. Error handling (catch all errors)
      .use(errorHandlingMiddleware())
      // 3. Logging (track all requests)
      .use(loggingMiddleware())
      // 4. CORS (for browser clients)
      .use(corsMiddleware())
      // 5. Rate limiting (prevent abuse)
      .use(rateLimitMiddleware())
      // 6. Request validation (validate content)
      .use(validationMiddleware());
}
Error handling middleware should be near the top of the middleware stack to catch errors from all downstream middleware and handlers.

Best Practices

1. Initialize Once

// Good: Initialize in root middleware
Handler middleware(Handler handler) {
  if (!Modular.isInitialized) {
    Modular.initialize(AppModule());
  }
  return handler.use(nestFrogMiddleware(AppModule()));
}

// Avoid: Multiple initializations
Handler middleware(Handler handler) {
  Modular.initialize(AppModule()); // May cause issues
  return handler.use(nestFrogMiddleware(AppModule()));
}

2. Order Middleware Logically

Handler middleware(Handler handler) {
  return handler
      .use(nestFrogMiddleware(AppModule()))  // First: DI
      .use(errorHandlingMiddleware())        // Second: Error handling
      .use(loggingMiddleware())              // Third: Logging
      .use(authMiddleware());                // Fourth: Auth
}

3. Use Services from Container

Middleware customMiddleware() {
  return (handler) {
    return (context) async {
      // Access services through Modular
      final service = Modular.get<MyService>();
      
      // Use the service
      await service.doSomething();
      
      return handler(context);
    };
  };
}

Build docs developers (and LLMs) love