spry 8.5.3
spry: ^8.5.3 copied to clipboard
Next-generation Dart server framework. Build modern servers and deploy them to the runtime you prefer.
Spry #
File-routing Dart server framework for teams that want one codebase across Dart VM, Node.js, Bun, Deno, Cloudflare Workers, Vercel, and Netlify.
Spry is built for a specific job:
- write server routes as files, not imperative registration code
- keep generated runtime output explicit and inspectable
- build the same project for multiple runtime targets
- generate OpenAPI documents and typed clients from the same source tree
If you want a Dart server framework that stays close to the filesystem, keeps deployment flexible, and does not hide the runtime behind a giant DSL, Spry is the fit.
Why Spry #
File routing first:routes/,middleware/,_middleware.dart, and_error.dartdefine the server shape directly from the project tree.Cross-runtime by design: target Dart VM, native snapshots, Node.js, Bun, Deno, Cloudflare Workers, Vercel, and Netlify without rewriting route code.Inspectable generated output: Spry emits concrete runtime files instead of burying behavior inside a black box.OpenAPI and client generation: keep API contracts, docs, and first-party typed clients aligned with the same route tree.
Start Here #
Quick start: install Spry, addroutes/, addspry.config.dart, rundart run spry serveRouting guide: learn params, wildcards, scoped middleware, and error boundariesDeploy guide: see how the same project targets Dart, Node, Bun, Deno, Cloudflare, Vercel, and NetlifyClient and OpenAPI: generate API docs and typed clients from the same app model
Quick Start #
Install the package:
dart pub add spry
Create a minimal project structure:
.
├─ routes/
│ └─ index.dart
└─ spry.config.dart
spry.config.dart
import 'package:spry/config.dart';
void main() {
defineSpryConfig(
host: '127.0.0.1',
port: 4000,
target: BuildTarget.vm,
);
}
routes/index.dart
import 'package:spry/spry.dart';
Response handler(Event event) {
return Response.json({
'message': 'hello from spry',
'runtime': event.context.runtime.name,
'path': event.url.path,
});
}
Start the dev server:
dart run spry serve
Core Ideas #
routes/defines request handlers with file routingmiddleware/and_middleware.dartshape cross-cutting request behavior_error.dartprovides scoped error handlingdefineHandler(...)adds handler-local middleware and error handlingpublic/serves static assets directlyspry.config.dartselects the runtime target and build behavior
What You Ship #
With Spry, the authoring model stays small:
- handlers return
Responsevalues directly - route structure comes from folders and filenames
- scoped middleware and errors stay near the routes they affect
- config decides the runtime target instead of per-route branching
What Spry generates:
- a concrete app definition you can inspect
- runtime entry files for the selected target
- target-specific wrappers for platforms like Cloudflare Workers or Vercel
OpenAPI #
Spry can generate an openapi.json document as part of the normal build
pipeline.
Use package:spry/config.dart for the build-side config and
package:spry/openapi.dart for the document objects:
import 'package:spry/config.dart';
import 'package:spry/openapi.dart';
void main() {
defineSpryConfig(
openapi: OpenAPIConfig(
document: OpenAPIDocumentConfig(
info: OpenAPIInfo(title: 'Spry API', version: '1.0.0'),
),
output: OpenAPIOutput.route('openapi.json'),
),
);
}
Route files can expose top-level openapi metadata:
import 'package:spry/openapi.dart';
final openapi = OpenAPI(
summary: 'List users',
tags: ['users'],
);
Key rules:
OpenAPIConfig.document.componentsdefines document-level components.- Route-level
OpenAPI(..., globalComponents: ...)is lifted into documentcomponentsduring generation. - A route without a method suffix expands to
GET,POST,PUT,PATCH,DELETE, andOPTIONSin OpenAPI. HEADis only emitted when a route explicitly defines.head.dart.OpenAPIOutput.route('openapi.json')writes the file intopublic/, so it is served like any other static asset.
Runtime Targets #
Spry can emit output for:
| Target | Runtime | Deploy Docs |
|---|---|---|
vm |
Dart VM | Dart VM |
exe |
Native executable | Native executable |
aot |
AOT snapshot | AOT snapshot |
jit |
JIT snapshot | JIT snapshot |
kernel |
Kernel snapshot | Kernel snapshot |
node |
Node.js | Node.js |
bun |
Bun | Bun |
deno |
Deno | Deno |
cloudflare |
Cloudflare Workers | Cloudflare Workers |
vercel |
Vercel | Vercel |
netlify |
Netlify Functions | Netlify Functions |
WebSockets #
Spry exposes websocket upgrades from the request event without introducing a second routing system.
import 'package:spry/spry.dart';
import 'package:spry/websocket.dart';
Response handler(Event event) {
if (!event.ws.isSupported || !event.ws.isUpgradeRequest) {
return Response('plain http fallback');
}
return event.ws.upgrade((ws) async {
ws.sendText('connected');
await for (final message in ws.events) {
switch (message) {
case TextDataReceived(text: final text):
ws.sendText('echo:$text');
case BinaryDataReceived():
case CloseReceived():
break;
}
}
}, protocol: 'chat');
}
Current websocket support follows the underlying osrv runtime surface:
- supported: Dart VM, Node.js, Bun, Deno, Cloudflare Workers
- unsupported: Vercel, current Netlify Functions runtime
Documentation #
Read the documentation at spry.medz.dev.
Start here:
License #
Sponsors #
Spry framework is an MIT licensed open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider sponsoring Seven(@medz) development.
Contributing #
Thank you to all the people who already contributed to Spry!