NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #815 most downloaded on pub.dev
A lightweight and flexible web server inspired by Shelf for building APIs and backend services.
Last release 7 days ago
30 Sep 2026
Release timing varies
gaps range from 9 days to 4 months
Nearly every release is documented
notes for 24 of 24 stable releases
Nothing withdrawn
no release was ever pulled
2 years old
28 releases · first in 2024
One column per month.
feat!: Add HTML form and multipart parsing (#380) - closes #379
BodyType has no const constructor. Drop const from BodyType(...) callsContent-Disposition parameters such as filename* are RFC 8187 ext-values and always encode as UTF-8. ContentDispositionParameter has no encoding, and its language is a LanguageTag. Parsing throws FormatException for a charset other than UTF-8 and for a malformed ext-valuereq.urlEncodedForm(), req.multipartForm() and req.formData(), which picks the parser from the request Content-Typereq.multipart() to stream parts without aggregating them. Each part is a MultipartFieldPart, MultipartFilePart or MultipartOtherPartIntFormField('age') with form.fields.get, tryGet and getAll, and files through FormFile. A missing field throws MissingFormFieldException, a value that does not decode throws InvalidFormFieldExceptionFormLimits caps body size, part, field and file counts, and field and file sizes. Going over one throws FormLimitExceededException, whose limit names the FormLimitMemoryUploadStorage by default. TempUploadStorage in relic_io writes each upload to its own directory. On POSIX systems the directory has mode 0700. On Windows it inherits the permissions of its parent, and the default parent, the user's temp directory, is private to that user. Call MultipartFormData.dispose() to delete them., .. and an empty filename give a null filename. filename* is ignored, per RFC 7578FormException with its statusCode, which is 400, 413 or 415. When parsing fails it also sends Connection: close, as the rest of the body may be unreadBodyType carries Content-Type parameters such as boundary in parameters. Body.fromData and Body.fromDataStream take a parameters argument, and the dart:io adapter carries them throughContentTypeHeader, read with headers.contentType. Set Content-Type through the bodyrefactor!: Drop NormalizedPath interning (#375) - fixes #118, #342, closes #344
NormalizedPath interning (#375) - fixes #118, #342, closes #344
NormalizedPath.interned is removed. Construction no longer caches, so there is no per-isolate cache to size, and no input that can thrash itRouter.lookupUri(method, url), the entry point for request routing: it derives the path with NormalizedPath.fromUri and looks it up with lookupPathWebSocketUpgrade(..., allowAnyOrigin: true) to accept them. Only the host is compared, since a TLS-terminating proxy changes scheme and port%2F) can no longer introduce a path boundary that no upstream proxy saw; NormalizedPath.fromUri names the safe conversionContent-Disposition parameter values and AuthenticationHeader challenge parts are escaped and validated, so a quote in a filename or realm can no longer end the value and graft on another parameter or challengeio.ContentType took separately and thus bypassed the CR/LF check applied to ordinary header valuesRandomconnectionsInfo(), drained and sent a 1001 close on graceful shutdown, and destroyed on close(force: true)use() on a catch-all route now applies to the prefix itself, so /api no longer bypasses the middleware that /api/keys runsToken68 for the RFC 9110 token68 form, used when validating a single-token WWW-Authenticate challengefeat: Add CacheControlHeader.parseStrict for validating own values
CacheControlHeader.parseStrict for validating own values (#370)
FormatException for an unrecognized directive or a malformed delta-seconds value instead of ignoring itparse remains lenient per RFC 9111 5.2Overhaul of typed HTTP headers for RFC compliance, landing in concert with serverpod 4.0. Parsing of received (request) headers is lenient (except sec
Overhaul of typed HTTP headers for RFC compliance, landing in concert with serverpod 4.0. Parsing of received (request) headers is lenient (except security-sensitive ones), while construction and serialization are always strict. Many typed headers changed shape and/or semantics, so this is a breaking release.
SetCookie now represents a single cookie, and SetCookieHeader is a List<SetCookie> collection (one Set-Cookie line per cookie, RFC 6265 4.1). The previous single-cookie SetCookieHeader(name:, value:, ...) API is replaced by SetCookie(...); augment a response with mh.setCookie = (mh.setCookie ?? const SetCookieHeader.empty()).add(cookie)Cookie header is parsed leniently: a malformed cookie is skipped and the header is rejected only when no cookie in it is usableCookieHeader.getCookies(name) returning every cookie with that name in order; byte-identical duplicates are preserved (not collapsed)Set-Cookie decode is strict (it is produced by the server): a malformed attribute rejects the cookieDomain is normalized per RFC 6265 5.2.3 — the full leading-dot run is stripped and the host is lower-cased; an all-dots Domain is rejectedMax-Age is parsed as a strict decimal integer per RFC 6265 5.2.2; non-decimal values such as 0xFF are rejected=value segments are dropped when parsing the request Cookie headerfeat: Make NormalizedPath interning cache configurable
NormalizedPath interning cache configurable (#343)
Cache<K, V> interfaceNoCache implementation for high-cardinality workloadsLruCache now implements CacheNormalizedPath.interned can be swapped to any Cache implementationstaticChangeCount and VM service extension to DeveloperTools (#341)DeveloperTools class to RelicApp (#340)docs(ai): Adds skills for AI agents based on documentation.
feat: Allow internal request forwarding with req.forwardTo(newReq)
Relic is now considered stable and production-ready! 🎉
Relic is now considered stable and production-ready! 🎉
fix: Downgrade meta dep to ^1.16.0 to match flutter 3.32.0 (serverpod lower bound)
feat: Add virtual host routing support
useHostWhenRouting parameter to routeWith() middlewareuseHostWhenRouting parameter to RelicApp{host}{path} for route matchingpaths, toString(), and toDot() to PathTrie for debugging (#320)force parameter to server close() method (#317)feat!: Add optional consume flag to attach
attach now throws ArgumentError when called with a tail path (/**), unless consume: true and the attached trie/router has a single value at the root (isSingle)group now throws ArgumentError when called with a tail pathconsume flag to attach that resets the attached trie's root after attachmentisSingle property to check if a trie/router has only a single root value/routeattach(..., consume: true) at tail paths (/**) when the attached trie/router isSingleinjectAt to use attach(..., consume: true), still enabling injection at tail paths for simple HandlerObjects (those adding a single handler at the root ('/'))fix: Close connection, if request too large
feat!: Add maxLength argument on read
feat!: Support back-tracking during routing
feat!: Add typed accessor for query and path parameters
refactor!: The Great Simplification
RequestContext into Request by moving the token propertyHandledContext renamed to ResultResponseContext into Response (now implements Result)respond(), hijack(), and connect() methods - handlers can construct and return Results directlyHandler = FutureOr<Result> Function(Request req)Context,RequestContext,RespondableContext,HijackableContext,ConnectableContext, andResponseContext interfacesConnectionContext to WebSocketUpgrade (now extends Result)HijackedContext to Hijack (now extends Result)feat: Expose noOfIsolates parameter on serve extension on RelicApp
feat: Add async RelicServer.connectionsInfo() method
connectionsInfo() method to RelicServer that returns the
current number of active, closing, and idle connections.ConnectionsInfo typedef is introduced as a record type with
active, closing, and idle fields.refactor!: Context renaming (#251) Renames core context types for improved clarity and consistency:
NewContext → RequestContextConnectContext → ConnectionContextHijackContext → HijackedContextRequestContext renamed to ContextRelicServer to enable concurrent request handling across multiple CPU cores. Adds noOfIsolates optional named parameter to RelicServer
constructor, and RelicApp.run.feat!: RelicApp now supports hot-reload
feat(router): Add support for setting up mappings of values on lookup router.use(path, map)
feat!: Custom CacheControl header per file
refactor!: Rename withResponse to respond (matches connect/hijack)
fix: Export bindHttpServer in io_adapter.dart
fix: Missing convenience getters and setter for Headers.xForwardedFor
feat: Implements lazy loading when parsing headers to avoid unnecessary validation.
RelicAddress type.Content-Length header conflicting with Transfer-Encoding: chunked.- First tech preview.
- Initial version.
Your coding agent can read these notes before it upgrades. Set up the MCP server →