--- outline: [2, 4] --- # Migrate from 1.x to 2.x This guide outlines the steps required to migrate your application to the latest version of Serinus. The framework has undergone significant refactoring to improve type safety, consistency, and modularity. ## Provider & Module Refactoring ### Rename `DeferredProvider` to `ComposedProvider` The way we handle providers that depend on other providers has changed to a "composition" model. - Update `Provider.deferred` to `Provider.composed`. - Update `DeferredProvider` to `ComposedProvider`. - Update the factory function to use `CompositionContext` and the `context.use()` syntax. ```dart class AppModule extends Module { AppModule(): super( providers: [ Provider.deferred( // [!code --] (AppProvider appProvider) => SecondProvider(appProvider), // [!code --] inject: [AppProvider], // [!code --] type: SecondProvider // [!code --] ), // [!code --] Provider.composed( // [!code ++] (CompositionContext context) async => SecondProvider(context.use()), // [!code ++] inject: [AppProvider], // [!code ++] ) // [!code ++] ] ) } ``` ### Global Scoping Global registration has moved from the Provider level to the Module level. - Remove `isGlobal` from your `Provider` classes. - Add `isGlobal => true` to the `Module` that should export its providers globally. ```dart class TestProvider extends Provider { @override bool get isGlobal => true; // [!code --] } class TestModule extends Module { @override bool get isGlobal => true; // [!code ++] } ``` ### Asyncrhonous Modules The `registerAsync` method must now explicitly return a `DynamicModule`. ```dart class AppModule extends Module { Future registerAsync() async { return DynamicModule( imports: [ // other modules ], providers: [ // providers ] ); } } ``` ## Controllers & Routing ### Required Path Parameters Controllers now require an explicit path in the constructor. - **Old**: `super(path: '/')` - **New**: `super('/')` ```dart class AppController extends Controller { AppController() : super(path: '/'); // [!code --] AppController() : super('/'); // [!code ++] } ``` ### Strict handler definitions Route handlers must now follow a strict signature: `Future Function(RequestContext context)`. This ensures better type safety and consistency. - Ensure all handlers are `async` and return a `Future`. - Custom parameters (like path params) can no longer be passed directly into the handler function; they must be accessed via `context`. ### Route hooks Routes no longer have lifecycle hooks. If you need route-specific logic, add hooks directly to the route's hooks list. ## Request & Response handling ### Unified Execution Context Middlewares, Pipes and Hooks now use `ExecutionContext` instead of raw request/response objects. This allows the same logic to work across HTTP, WebSockets, and other protocols. - Use `context.argumentsHost` to access the specific protocol host (e.g., `HttpArgumentsHost`). ### Body Parsing Rework The `ParseSchema` pipe and `schema` parameters in routes have been removed. ```dart on( Route.post( '/data', pipes: {} // [!code ++] ), schema: AcanthisParseSchema(), // [!code --] (context) async { return data; } ); ``` - Use the new `context.bodyAs()` method to parse bodies into a specific type `T`. - Standardize on using Pipes for any custom validation logic. ```dart on(Route.post('/json'), (context) async { final body = context.bodyAs>(); // to parse the body as JSON return body; }); on(Route.post('/text'), (context) async { final body = context.bodyAs(); // to parse the body as plain text return body; }); on(Route.post('/form'), (context) async { final body = context.bodyAs(); // to parse the body as form data return body; }); ``` ### Header - **Headers**: now handled via a dedicated `Headers` class for better structure. - **Status Codes**: POST requests now return `201 Created` by default instead of `200 OK`. ## Middleware & Hooks ### Fluent Middleware API Middlewares are no longer registered in the `Module` constructor. - Implement the `configure(MiddlewareConsumer consumer)` method in your module. - Use the fluent `.apply().forRoutes()` syntax. ```dart class AppModule extends Module { AppModule() : super( middlewares: [ // [!code --] LogMiddleware( // [!code --] routes: ['*'] // [!code --] ) // [!code --] ] // [!code --] ); void configure(MiddlewareConsumer consumer) { // [!code ++] consumer.apply(LogMiddleware()).forRoutes([ // [!code ++] RouteInfo( // [!code ++] '*' // [!code ++] ) // [!code ++] ]); // [!code ++] } // [!code ++] } ``` ### Hook Separation The `OnRequestResponse` mixin has been split. - Use `OnRequest` and `OnResponse` separately. - Update method signatures to use `ExecutionContext` and `WrappedResponse` where applicable. ```dart Future onRequest(Request request, InternalResponse response); // [!code --] Future onRequest(ExecutionContext context); // [!code ++] Future onResponse(Request request, dynamic data, ResponseProperties properties); // [!code --] Future onResponse(ExecutionContext context, WrappedResponse data); // [!code ++] Future afterHandle(RequestContext context, dynamic response); // [!code --] Future afterHandle(ExecutionContext context, WrappedResponse response); // [!code ++] Future beforeHandle(RequestContext context); // [!code --] Future beforeHandle(ExecutionContext context); // [!code ++] ``` ## Utilities & Services ### Logger Refactor The logging API has been updated for better flexibility. - Rename `loggerService` to `logger`. - Change `loggingLevel` (single value) to `logLevels` (a `Set`). ```dart void main(List arguments) async { final application = await serinus.createApplication( entrypoint: AppModule(), host: InternetAddress.anyIPv4.address, loggerService: null, // [!code --] loggingLevel: LogLevel.info // [!code --] logger: ConsoleLogger(prefix: 'Serinus New Logger'), // [!code ++] logLevels: {LogLevel.info} // [!code ++] ); await application.serve(); } ``` ### Exceptions The `message` parameter in `SerinusException` is now a required positional argument rather than a named one. - **New**: `throw BadGatewayException('Message')`. ```dart throw BadGatewayException(message: 'Failed to retrieve template'); // [!code --] throw BadGatewayException('Failed to retrieve template'); // [!code ++] ``` ### View Engine Simplification - `View` and `ViewString` are merged into one `View` class. - Use `View.template()` or `View.string()` constructors. - View Engines now only need to implement a single `render` method. ```dart class MustacheViewEngine extends ViewEngine { Future render(View view) async {} Future renderString(ViewString view) async {} // [!code --] } // Usage in controller class AppController extends Controller { AppController() : super('/') { on(Route.get('/template'), (context) async { return View('template', {}); // [!code --] return View.template('templateName', {}); // [!code ++] }); on(Route.get('/string'), (context) async { return View('string', {}); // [!code --] return View.string('string', {}); // [!code ++] }); } } ``` ## Deprecations & Removals - **Tracer API**: Removed (to be reintroduced later). - **ModelProvider**: Now uses `String` keys instead of `Type` for registration. - **ResponseProperties**: Renamed to `ResponseContext`.