Skip to content

Migration from Aqueduct 2 to Aqueduct 3.0

Aqueduct 3 makes a number of breaking changes from Aqueduct 2. Some of these changes are changes to behavior, and others are simple API renaming. This guide demonstrates the changes required for commonly used code.

RequestSink is now ApplicationChannel

This type has been renamed to ApplicationChannel and its methods for initializing an application have changed. The method setupRouter has been replaced by the getter entryPoint. All controller creating code should be located in this getter, and you must now create the Router yourself if you choose to. Additionally, the methods to link together controllers (e.g., generate, pipe) have been replaced with the link method, which always takes a closure.

class Channel extends ApplicationChannel {
  Service service;

  Future prepare() async {
    service = Service(options.context["service"]);

  Controller get entryPoint {
    final router = Router(); => MyController(service));
    return router;

The object returned by this getter is the first controller to receive a request, and does not have to be a router (e.g., it could be global middleware). By default, the closure provided to link is invoked once at startup and the same controller instance is reused for all requests. If a Controller implements Recyclable<T>, the closure is invoke for each new request and a new controller is created to handle the request.

You are no longer required to implement a constructor for ApplicationChannel. All of your initialization should be provided by overriding prepare in your channel. You will have access to configuration data through an options property in both prepare and entryPoint.

HTTPController is now ResourceController

The name of this type has changed, and the syntax for identifying operation methods and binding values has improved.

class MyController extends ResourceController {
  Future<Response> getAll({@Bind.query("filter") String filter}) async {

  Future<Response> updateThing(@Bind.path('id') int id, @Bind.body() Thing thing) async {

Operation methods must now be decorated an Operation annotation; this replaces metadata like @httpGet. For an operation method to match a request with path variables, the names of those path variables must be arguments to the Operation constructor. In previous versions, path variable methods were selected if the method's arguments bound a path variable. This is no longer the case - binding a path variable has no impact on the selection of a method, the path variable must be identified in the Operation. You no longer have to bind a path variable and can retrieve it through the request.path.

Bound parameters are identified by the Bind annotation, and the type of binding is identified by the constructor used. This syntax replaces HTTPQuery.bind(), etc.

Query.where syntax has changed

Previously, query filters were applied by assigning expressions like whereEqualTo to properties of Query.where. This has been replaced with the property selector syntax that is used when joining, sorting or paging by a property.

final query = Query<User>()
  ..where((u) =>;

Methods like whereEqualTo no longer exist - all expressions to apply to a selected property are instance methods of the object returned by where.

Test library is now aqueduct_test

The test library is now a separate library named aqueduct_test and must be added to your pubspec.yaml. Much of its behavior has changed to make writing tests more effective. See the documentation for more details.

Swagger -> OpenAPI

Aqueduct had experimental support for Swagger documentation generation. It now has full, tested support for OpenAPI 3 documentation generation. See the documentation for more details.


The following common signatures are a non-exhaustive list of simple API renaming:

Authorization.resourceOwnerIdentifier -> Authorization.ownerID
Request.innerRequest -> Request.raw
AuthStorage -> AuthServerDelegate -> AuthServer.delegate
ApplicationConfiguration -> ApplicationOptions
Application.configuration -> Application.options
ServiceRegistry -> ServiceRegistry
ManagedTableAttributes -> Table
ManagedRelationshipDeleteRule -> DeleteRule
ManagedRelationship -> Relate
ManagedColumnAttributes -> Column
managedPrimaryKey -> primaryKey
ManagedTransientAttribute -> Serialize
Serialize now replaces managedTransientAttribute, managedTransientInputAttribute, and managedTransientOutputAttribute.
RequestController -> Controller
RequestController.processRequest -> Controller.handle
HTTPController -> ResourceController
Router.unhandledRequestController -> Router.unmatchedController