Skip to content
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## Upcoming Release
- **refactor!**: shift to eager loading for a robust, fail-fast startup.

## 1.0.1 - 2025-10-17

- **chore**: A new migration ensures that existing user preference documents are updated to include the savedFilters field, initialized as an empty array.
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ Click on any category to explore.

---

### ✨ Eager Loading & Fail-Fast Startup
- **Robust Initialization:** The server now initializes all critical dependencies (database connection, migrations, seeding) *before* it starts accepting any requests.
- **Fail-Fast by Design:** If any part of the startup process fails (e.g., the database is down), the server will immediately exit with a clear error.
> **Your Advantage:** This eliminates startup race conditions and ensures the server is either fully operational or not running at all. It provides a highly predictable and stable production environment, preventing the server from running in a broken state.

---

### 🔌 Robust Dependency Injection
- **Testable & Modular:** A centralized dependency injection system makes the entire application highly modular and easy to test.
- **Swappable Implementations:** Easily swap out core components—like the database (MongoDB), email provider (SendGrid), or storage services—without rewriting your business logic.
Expand Down
85 changes: 85 additions & 0 deletions bin/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// ignore_for_file: avoid_print

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

import 'package:dart_frog/dart_frog.dart';
import 'package:flutter_news_app_api_server_full_source_code/src/config/app_dependencies.dart';
import 'package:logging/logging.dart';

// Import the generated server entrypoint to access `buildRootHandler`.
import '../.dart_frog/server.dart' as dart_frog;

/// The main entrypoint for the application.
///
/// This custom entrypoint implements an "eager loading" strategy. It ensures
/// that all critical application dependencies are initialized *before* the
/// HTTP server starts listening for requests.
///
/// If any part of the dependency initialization fails (e.g., database
/// connection, migrations), the process will log a fatal error and exit,
/// preventing the server from running in a broken state. This is a robust,
/// "fail-fast" approach.
Future<void> main(List<String> args) async {
// Use a local logger for startup-specific messages.
// This is also the ideal place to configure the root logger for the entire
// application, as it's guaranteed to run only once at startup.
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
final message = StringBuffer()
..write('${record.level.name}: ${record.time}: ${record.loggerName}: ')
..writeln(record.message);

if (record.error != null) {
message.writeln(' ERROR: ${record.error}');
}
if (record.stackTrace != null) {
message.writeln(' STACK TRACE: ${record.stackTrace}');
}

// Write the log message atomically to stdout.
stdout.write(message.toString());
});

final log = Logger('EagerEntrypoint');
HttpServer? server;

Future<void> shutdown([String? signal]) async {
log.info('Received ${signal ?? 'signal'}. Shutting down gracefully...');
// Stop accepting new connections.
await server?.close();
// Dispose all application dependencies.
await AppDependencies.instance.dispose();
log.info('Shutdown complete.');
exit(0);
}

// Listen for termination signals.
ProcessSignal.sigint.watch().listen((_) => shutdown('SIGINT'));
// SIGTERM is not supported on Windows. Attempting to listen to it will throw.
if (!Platform.isWindows) {
ProcessSignal.sigterm.watch().listen((_) => shutdown('SIGTERM'));
}

try {
log.info('EAGER_INIT: Initializing application dependencies...');

// Eagerly initialize all dependencies. If this fails, it will throw.
await AppDependencies.instance.init();

log.info('EAGER_INIT: Dependencies initialized successfully.');
log.info('EAGER_INIT: Starting Dart Frog server...');

// Start the server directly without the hot reload wrapper.
final address = InternetAddress.anyIPv6;
final port = int.tryParse(Platform.environment['PORT'] ?? '8080') ?? 8080;
server = await serve(dart_frog.buildRootHandler(), address, port);
log.info(
'Server listening on http://${server.address.host}:${server.port}',
);
} catch (e, s) {
log.severe('EAGER_INIT: FATAL: Failed to start server.', e, s);
// Exit the process if initialization fails.
exit(1);
}
}
Loading
Loading