nest_frog

Dart Frog integration for Nest Dart - bringing dependency injection and modular architecture to Dart Frog backend applications.

Features

  • 🐸 Dart Frog Integration - Seamless middleware integration
  • 🔄 Request Context - Service resolution from request context
  • 🎯 Unified API - Clean Modular API for backend services
  • Performance - Minimal overhead with efficient service resolution
  • 🧪 Testing Support - Easy mocking and testing utilities

Installation

Add nest_frog to your pubspec.yaml:

dependencies:
  nest_frog: ^0.1.1
  dart_frog: ^1.1.0

Quick Start

1. Create Your Modules

import 'package:nest_frog/nest_frog.dart';

class AppModule extends Module {
  @override
  List<Module> get imports => [CoreModule(), UserModule()];
  
  @override
  void providers(Locator locator) {
    // App-level services
  }
}

2. Set Up Middleware

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

3. Use Services in Routes

// routes/users/index.dart
import 'package:dart_frog/dart_frog.dart';
import 'package:nest_frog/nest_frog.dart';

Future<Response> onRequest(RequestContext context) async {
  final userService = Modular.of(context).get<UserService>();
  
  switch (context.request.method) {
    case HttpMethod.get:
      final users = userService.getAllUsers();
      return Response.json(body: {
        'users': users.map((u) => u.toJson()).toList(),
      });
    case HttpMethod.post:
      final body = await context.request.json();
      final user = userService.createUser(body['name'], body['email']);
      return Response.json(statusCode: 201, body: user.toJson());
    default:
      return Response(statusCode: 405);
  }
}

Service Access Patterns

Future<Response> onRequest(RequestContext context) async {
  final modular = Modular.of(context);
  final userService = modular.get<UserService>();
  final logger = modular.get<LoggerService>();
  
  logger.log('Processing request');
  // Handle request...
}

Direct Access

Future<Response> onRequest(RequestContext context) async {
  final configService = Modular.get<ConfigService>();
  final apiKey = configService.apiKey;
  
  // Use global configuration...
}

Complete Example

Services

class UserService {
  final UserRepository _repository;
  final LoggerService _logger;
  
  UserService(this._repository, this._logger);
  
  List<User> getAllUsers() {
    _logger.log('Getting all users');
    return _repository.findAll();
  }
  
  User createUser(String name, String email) {
    _logger.log('Creating user: $name');
    return _repository.create(name, email);
  }
}

Module Configuration

class UserModule extends Module {
  @override
  List<Module> get imports => [CoreModule(), DatabaseModule()];
  
  @override
  void providers(Locator locator) {
    locator.registerSingleton<UserRepository>(
      UserRepository(locator.get<DatabaseService>()),
    );
    
    locator.registerSingleton<UserService>(
      UserService(
        locator.get<UserRepository>(),
        locator.get<LoggerService>(),
      ),
    );
  }
  
  @override
  List<Type> get exports => [UserService];
}

Route Handlers

Future<Response> onRequest(RequestContext context) async {
  final userService = Modular.of(context).get<UserService>();
  
  try {
    switch (context.request.method) {
      case HttpMethod.get:
        return _getUsers(userService);
      case HttpMethod.post:
        return await _createUser(context, userService);
      default:
        return Response(statusCode: 405);
    }
  } catch (e) {
    return Response.json(
      statusCode: 500,
      body: {'error': 'Internal server error'},
    );
  }
}

Response _getUsers(UserService userService) {
  final users = userService.getAllUsers();
  return Response.json(body: {
    'users': users.map((u) => u.toJson()).toList(),
    'count': users.length,
  });
}

Advanced Features

Middleware Integration

Middleware authMiddleware() {
  return (handler) {
    return (context) async {
      final authService = Modular.get<AuthService>();
      final token = context.request.headers['authorization'];
      
      if (!authService.validateToken(token)) {
        return Response.json(
          statusCode: 401,
          body: {'error': 'Unauthorized'},
        );
      }
      
      return handler(context);
    };
  };
}

Error Handling

class ApiResponse {
  static Response success(Map<String, dynamic> data) {
    return Response.json(body: {
      'success': true,
      'data': data,
      'timestamp': DateTime.now().toIso8601String(),
    });
  }
  
  static Response error(String message, {int statusCode = 400}) {
    return Response.json(
      statusCode: statusCode,
      body: {
        'success': false,
        'error': message,
        'timestamp': DateTime.now().toIso8601String(),
      },
    );
  }
}

Testing

Test your routes with mock services:

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

void main() {
  group('User API', () {
    test('GET /users returns users', () async {
      // Initialize with test module
      await Modular.initialize(TestModule());
      
      final request = Request.get(Uri.parse('/users'));
      final context = RequestContext(request: request);
      
      final response = await onRequest(context);
      
      expect(response.statusCode, equals(200));
      // Additional assertions...
    });
  });
}

class TestModule extends Module {
  @override
  void providers(Locator locator) {
    locator.registerSingleton<UserService>(MockUserService());
  }
}

Performance Tips

  • Use lazy singletons for expensive services
  • Implement proper service disposal
  • Cache frequently accessed services
  • Use request-scoped services when appropriate

Documentation

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Libraries

nest_frog
Dart Frog integration for Nest Dart - bringing dependency injection and modular architecture to Dart Frog backend applications.