dart_server 1.0.3
dart_server: ^1.0.3 copied to clipboard
A NestJS-style backend framework for Dart with modules, dependency injection, guards and an Express-like core. Zero external dependencies.
Changelog #
1.0.3 #
API documentation (OpenAPI) #
app.useOpenApi()— serves an interactive documentation UI at/docsand the generated OpenAPI 3.0 specification at/docs/openapi.json, built automatically from the route table (methods, paths, path parameters).ApiDoc— optionaldoc:argument on every route registration (controller and Express-style) for summaries, descriptions, tags, parameter/body/response docs, security references,deprecatedandhidden.ApiSchema— a minimal JSON-Schema builder (object/string/integer/number/boolean/array/raw) for payload shapes, plusApiParam,ApiBodyandApiResponse.OpenApiGeneratoris exported for offline spec generation (e.g. CI).ApiSecurityScheme(apiKey/bearer/basic/raw) — declare authentication onuseOpenApi(securitySchemes: ...)and reference it withApiDoc(security: [...]); the docs UI shows an Authorize button and sends the credential with try-it-out requests.- The scaffold (
dart_server create) wiresuseOpenApiand documents its routes out of the box.
Logging & startup #
- The startup banner now prints the API docs and dev-dashboard URLs (linking
via
localhostwhen bound to all interfaces). logger()no longer logs the dev dashboard's own polling traffic (marked with the newinternalRequestMarkercontext key); opt back in withlogInternal: true. NewignorePaths:option silences other noisy endpoints (e.g./health) by path prefix.
1.0.2 #
NestJS-parity release: the full request pipeline, lifecycle and configuration — still zero dependencies, no reflection, no code generation.
Request pipeline #
- Guards (
Guard, theCanActivateequivalent): attach globally (DartServerFactory.create(root, guards: [...])), per controller (List<Guard> get guards) or per route (routes.get('/', h, guards: [...])).false→403; throw anHttpErrorfor other statuses.Guard.from((req) => ...)for inline guards. - Interceptors: wrap handler execution after guards pass (Nest's
lifecycle order) — same
(req, next)shape as middleware; attach globally, per controller or per route. - Scoped middleware: controller- and route-level middleware, running before guards (Express-style position, like Nest middleware).
- Exception filters (
ExceptionFilter): convert errors into responses, tried most-specific-first (route → controller → global); returnnullto decline. Unhandled errors fall through toonError/HttpErrormapping.
Lifecycle & configuration #
OnShutdown— awaited in reverse creation order when the app closes; pairs with the existingOnInit.- Graceful shutdown —
app.close()now drains in-flight requests before running hooks, is safe under concurrent calls (one shared teardown future), and a failing hook never blocks the rest. app.enableShutdownHooks()— run shutdown hooks on SIGINT/SIGTERM (Ctrl-C, container stop), Nest-style. A second signal force-closes connections and stops intercepting, so a stuck shutdown can always be escaped.app.addShutdownHook()registers your own hooks.Env— zero-dep.env+Platform.environmentconfiguration (Env.load(),env['KEY'],require,getInt,getBool); the real environment wins over file values.
Ergonomics #
- Typed request helpers (the ParseIntPipe equivalents):
req.paramInt(),req.param(),req.queryInt(),req.queryBool(), andreq.jsonMap()— validation failures become400s instead of500s. HttpError.tooManyRequests(429) andHttpError.serviceUnavailable(503).OnInit/OnShutdown/Guard/ExceptionFilterareinterfaceclasses — implement, don't extend.
CLI #
createnow scaffolds the full Nest-style starter:app_module+app_controller+app_service(with DI),.env.example, a unit test and an end-to-end test, andenableShutdownHooks()wired inbin/server.dart.- New generators:
make:guard,make:interceptor,make:filter.
1.0.1 #
- Render the dev-dashboard URL as code instead of an
http://link so the README passes pub.dev's secure-links check. - Point
repository/issue_trackerat the canonical GitHub repository.
1.0.0 #
Initial release.
Modular architecture (NestJS-style, optional) #
Module— groupsprovidersandcontrollers, withimports/exportsand per-module provider encapsulation (plusisGlobal).Provider— dependency injection withsingleton,transientandvaluescopes, resolved viaInjector.get<T>(); missing providers and circular dependencies fail fast withDiError.Controller— class-based route grouping under abasePathviaRouteRegistrar, with constructor injection.OnInit— async lifecycle hook awaited during bootstrap in dependency order.DartServerFactory.create(rootModule)— wires the graph and mounts every controller's routes; returns an ordinaryDartServer.- No decorators, reflection or codegen — wiring is plain, analyzable Dart.
- CLI:
dart_server createscaffolds a modular app;make:resource/make:module/make:controller/make:service/make:repository/make:modelgenerate feature modules underlib/modules/<name>/.
Routing & requests #
- Express-style
DartServerwithget/post/put/delete/patch/head/options/all. - Path parameters (
/users/:id) and a trailing wildcard (/files/*); a*in any non-final segment is rejected at registration. - Automatic
HEAD→GETfallback with the response body stripped. Requestwithpath,method,headers,query,params,bodyBytes,body,context, and a lazy, cachedjson().- Raw
bodyBytesalways preserved (binary-safe);bodydecodes UTF-8 with malformed bytes replaced rather than thrown;json()re-throws on every call for invalid JSON instead of cachingnull. - Configurable
maxBodyBytes(default 1 MiB) — oversized bodies are rejected with413before any handler runs. - Malformed percent-encoding in a path segment falls back to the raw value
instead of producing a
500.
Responses #
Response.json,text,html,status,bytes,redirect, fluent.header().HEADresponses send theContent-Lengthbut no body;204/304/1xxresponses send neither a body nor aContent-Length.
Middleware & errors #
- Global middleware with
next()chaining and per-requestcontext. - Handler errors are converted to responses inside the chain, so middleware
(and
logger/cors) observe error responses too. HttpErrorwith status-mapped constructors; customizableonError.
CLI (dart_server) #
dart_server create <name>— scaffolds a ready-to-run app (entry point, app wiring, routes, controllers/models/repositories layout) and runspub get.dart_server dev— runs withDART_SERVER_ENV=developmentand auto-restarts on.dartchanges underlib//bin/;dart_server prodruns in production.dart_server make:model|controller|repository|middleware|service|resource— code generators with name normalization (snake/camel/Pascal) and--force.- Installable via
dart pub global activate dart_server; zero dependencies (hand-rolled argument parsing).
Dev tools #
app.useDevTools()— an in-process development dashboard at/__devthat tracks recent requests (method, path, status, timing, headers, request/ response bodies), aggregate stats, the live route table and server info, with a JSON snapshot at/__dev/api. Self-contained (no external assets).- Development-only: disabled when
DART_SERVER_ENV/DART_ENV/ENVis production-like; configurable mount path, buffer size and body capture.
Bundled middleware #
logger()— logs successful and errored requests.cors()— secure by default:credentials: truewith a wildcard origin is refused; use theoriginsallow-list for credentialed cross-origin access. Pre-flights are detected byAccess-Control-Request-Methodso explicitapp.options(...)routes still run.serveStatic()— rejects..and symlink path-traversal (real target re-checked against the root).
Other #
- Zero external runtime dependencies (built on
dart:io+dart:convert). listen(..., quiet: true)suppresses the startup banner.