Migration Guide
Upgrade to v8.2
Spry 8.2 changes the generated output layout, renames the Dart runtime target, and removes the wildcard-param alias that named catch-all routes used to receive.
The breaking changes are:
BuildTarget.dartis nowBuildTarget.vm- generated Dart source moved from
.spry/*.dartto.spry/src/*.dart - JS target entrypoints moved to deploy-facing filenames such as
.spry/node/index.cjsand.spry/cloudflare/index.js - named catch-all routes no longer mirror their value onto
event.params.wildcard
BuildTarget.dart -> BuildTarget.vm
Update your spry.config.dart target selection:
// Before
defineSpryConfig(target: BuildTarget.dart);
// After
defineSpryConfig(target: BuildTarget.vm);Generated output layout
If your deployment scripts or tooling execute generated files directly, update their paths:
.spry/main.dart->.spry/src/main.dart.spry/app.dart->.spry/src/app.dart.spry/hooks.dart->.spry/src/hooks.dart.spry/node/main.cjs->.spry/node/index.cjs.spry/bun/main.js->.spry/bun/index.js.spry/deno/main.js->.spry/deno/index.js.spry/cloudflare/cloudflare.mjs->.spry/cloudflare/index.js
Spry 8.2 also adds native Dart build targets for direct deployment:
BuildTarget.exeBuildTarget.aotBuildTarget.jitBuildTarget.kernel
Named catch-all params
Named catch-all route params no longer mirror their value onto event.params.wildcard. Read the remainder through the declared param name instead:
// Before
final slug = event.params.wildcard;
// After
final slug = event.params.get('slug');This applies to named catch-all filesystem routes such as routes/[...slug].dart, which continue to map to /**:slug.
What to verify after upgrading
- Update
spry.config.dartto useBuildTarget.vm. - Rebuild and re-check any deployment script, Dockerfile, or platform config that points at generated files under
.spry/. - Replace
event.params.wildcardwith the declared catch-all param name in route handlers.
Upgrade to v8
If you are already on the file-routing model introduced in Spry 7, the move to v8 is small but not zero-cost.
The breaking changes are:
- direct
Requestconstruction now follows the upstream Fetch-style init API - direct
Responseconstruction now follows the upstream Fetch-style init API - manual remainder route strings must use
/**instead of/*
Direct Request construction
If you construct exported Request values yourself, switch to RequestInit:
// Before
final request = Request(
Uri.parse('https://example.com/users/42'),
method: 'GET',
);
// After
final request = Request(
Uri.parse('https://example.com/users/42'),
RequestInit(method: HttpMethod.get),
);Direct Response construction
If you build Response values directly, move status and headers into ResponseInit:
// Before
return Response(
status: 404,
headers: {'content-type': 'application/json'},
body: '{"error":"not_found"}',
);
// After
return Response(
'{"error":"not_found"}',
ResponseInit(
status: 404,
headers: {'content-type': 'application/json'},
),
);Spry re-exports RequestInit and ResponseInit from package:spry/spry.dart and package:spry/app.dart, so most projects only need to update constructor calls.
Manual string routes
If you manually construct Spry, MiddlewareRoute, or ErrorRoute with string paths, change remainder matches from /* to /**:
// Before
MiddlewareRoute(path: '/*', handler: requestLogger)
ErrorRoute(path: '/api/*', handler: apiError)
// After
MiddlewareRoute(path: '/**', handler: requestLogger)
ErrorRoute(path: '/api/**', handler: apiError)Filesystem routes do not need a rename for this change. Spry will keep translating file names into the updated roux syntax for you.
What to verify after upgrading
- Re-run route matching tests if you use manual
Spry(...)route maps. - Rebuild any helper code that manually creates
RequestorResponseobjects. - Check middleware and error scopes if they relied on
/*remainder matching.
New routing syntax you can opt into
v8 also adds richer filesystem route syntax. These are additive features, not migration requirements:
- regex params such as
[id([0-9]+)] - optional params such as
[[id]] - repeated params such as
[...path+]and[[...path]] - embedded params such as
post-[id].json - single-segment wildcards such as
[_]
Upgrade from v6 to v7
Spry 7 introduced a different center of gravity than older Spry guides. The framework moved to file routing, generated output, and runtime targets selected in config.
The headline shift
Old docs were centered on:
createSpry()- imperative route registration such as
app.get(...) - group-based route composition
- standalone server examples written by hand
Spry 7 is centered on:
routes/as the source of truthspry buildandspry servedefineSpryConfig(...)- a generated
Spry(...)app and target-specific runtime entrypoint
What to replace in v7
Imperative route registration
If you previously wrote app.get('/users/:id', handler);, move that logic into routes/users/[id].dart.
Route groups
If you relied on app.group(...), stop modeling route structure in code. Use folders plus _middleware.dart for scope-level behavior.
Manual server adapters
If your mental model was “I write the server wrapper myself”, move that concern into spry.config.dart and target selection.
Request helpers
Prefer the request-scoped Event object:
event.requestevent.headersevent.paramsevent.localsevent.context
A migration path that usually works
- Move route logic into
routes/. - Add
spry.config.dart. - Replace shared wrappers with
middleware/and scoped_middleware.dart. - Replace ad-hoc error translation with scoped
_error.dart. - Run
dart run spry serveand inspect the generated output.