Upgrade to 3.0
The new authentication module has separate migration documentation. This guide covers only core framework changes.
Requirementsβ
- Dart SDK: 3.8.0+
- Flutter SDK: 3.32.0+
Breaking changes summaryβ
- Web server: Relic framework integration (custom routes need updates)
- Session.request: New property to access HTTP request from endpoints
- Authentication:
AuthenticationInfochanges,session.authenticatednow synchronous - Enum serialization: Default changed from
byIndextobyName - Model changes:
SerializableEntityremoved, YAML keywords updated
Updating your projectβ
Update the CLIβ
Update the Serverpod command line interface to the latest version:
dart pub global activate serverpod_cli
Verify the installed version:
serverpod version
Update pubspec.yaml filesβ
Update the pubspec.yaml files in your server, client, and flutter directories. Change all Serverpod package versions to 3.0.0 (or later 3.x version).
Update the Dart SDK constraint in all pubspec.yaml files:
environment:
sdk: '>=3.8.0 <4.0.0'
Update the Dockerfileβ
Update the Dart version in your project's Dockerfile:
FROM dart:3.8 AS build
...
Run updatesβ
After updating your pubspec.yaml files, run these commands in each package directory:
dart pub upgrade
Then regenerate your project in your server directory:
serverpod generate
Create and apply migrationsβ
A database migration is required for Serverpod 3.0. The session log table now stores authenticated user IDs as String instead of int to support non-numeric identifiers (e.g., UUIDs).
Create and apply the migration:
serverpod create-migration
dart run bin/main.dart --apply-migrations
Migration checklistβ
- Update CLI, pubspec files, and Dockerfile (see above)
- Update custom Route classes (see Web Server section)
- Update enum serialization strategy (see Enum section)
- Replace
SerializableEntitywithSerializableModelin custom models - Update custom
AuthenticationInfousage (see Authentication section) - Test and deploy
Web server (Relic framework)β
Serverpod 3.0 integrates the Relic framework for web server functionality, bringing it out of experimental status. This provides improved performance, better request/response handling, and built-in WebSocket support.
Route.handleCall signatureβ
| Aspect | Serverpod 2.x | Serverpod 3.0 |
|---|---|---|
| Request | HttpRequest request | Request request |
| Return | Future<bool> | FutureOr<Result> |
| Response | return true | return Response.ok() |
Before:
import 'dart:io';
class MyRoute extends Route {
Future<bool> handleCall(Session session, HttpRequest request) async {
request.response.write('<html><body>Hello</body></html>');
return true;
}
}
After:
class MyRoute extends Route {
FutureOr<Result> handleCall(Session session, Request request) async {
return Response.ok(
body: Body.fromString('<html><body>Hello</body></html>', mimeType: MimeType.html),
);
}
}
Changes:
HttpRequestβRequest(from Relic)Future<bool>βFutureOr<Result>request.remoteIpAddressβrequest.remoteInfo(both returnString)request.headers.value('name')βrequest.headers['name']- Return
Responseinstead ofbool
Widget class namesβ
The web server widget classes have been reorganized for better clarity. Legacy class names are deprecated but still available for backward compatibility.
| Old (Deprecated) | New |
|---|---|
AbstractWidget | WebWidget |
Widget | TemplateWidget |
WidgetList | ListWidget |
WidgetJson | JsonWidget |
WidgetRedirect | RedirectWidget |
The WidgetRoute class remains unchanged and continues to be the base class for web routes.
Before:
Future<Widget> build(...) => Widget(name: 'page')..values = {...};
After:
Future<WebWidget> build(...) => TemplateWidget(name: 'page', values: {...});
Static route updatesβ
The RouteStaticDirectory class has been deprecated in favor of StaticRoute.directory():
Before:
pod.webServer.addRoute(
RouteStaticDirectory(
serverDirectory: 'web/static',
basePath: '/',
),
'/static/**',
);
After:
pod.webServer.addRoute(
StaticRoute.directory(Directory('web/static')),
'/static/**',
);
The new StaticRoute provides better cache control options. You can use the built-in static helper methods for common caching scenarios:
pod.webServer.addRoute(
StaticRoute.directory(
Directory('web/static'),
cacheControlFactory: StaticRoute.publicImmutable(maxAge: 3600),
),
'/static/**',
);
Available cache control factory methods:
StaticRoute.public(maxAge: seconds)- Public cache with optional max-ageStaticRoute.publicImmutable(maxAge: seconds)- Public immutable cache with optional max-ageStaticRoute.privateNoCache()- Private cache with no-cache directiveStaticRoute.noStore()- No storage allowed
You can also provide a custom factory function:
pod.webServer.addRoute(
StaticRoute.directory(
Directory('web/static'),
cacheControlFactory: (ctx, fileInfo) => CacheControlHeader(
publicCache: true,
maxAge: 3600,
immutable: true,
),
),
'/static/**',
);
Session.request propertyβ
With the Relic framework integration, the Session object now provides access to the underlying HTTP request through the request property. This allows endpoint methods to access request metadata such as client IP address and headers directly from the session.
Use optional chaining as it's null on some session types (e.g., sessions created for background tasks).
class MyEndpoint extends Endpoint {
Future<String> getClientInfo(Session session) async {
final ip = session.request?.remoteInfo ?? 'unknown';
final userAgent = session.request?.headers.userAgent ?? 'unknown';
return 'IP: $ip, UA: $userAgent';
}
}
Authenticationβ
Serverpod 3.0 includes several changes to the authentication system that improve type safety and performance.
AuthenticationInfo changesβ
The AuthenticationInfo class has been updated:
authIdis now non-nullable (previously optional)userIdentifierparameter type changed fromObjecttoString
session.authenticated is now synchronousβ
Authentication is now resolved when the session is created, making session.authenticated synchronous. This improves performance by eliminating repeated async lookups.
Before (async):
final auth = await session.authenticated;
After (sync):
final auth = session.authenticated;
Client auth key providerβ
The authKeyProvider interface replaces the previous authenticationKeyManager. This interface has been simplified to make it more explicit what the client needsβit now only requires something that can provide an auth key wrapped as a header.
Before:
Client(host)..authenticationKeyManager = myManager;
After:
Client(host)..authKeyProvider = myProvider;
Enum serializationβ
The default enum serialization strategy has changed from byIndex to byName. This change improves robustness when reordering or adding enum values, as serialized data remains valid even if the enum definition changes. With byName, the string representation is stored instead of the numeric index, making your data more resilient and easier to debug.
Keep old behavior:
enum: UserRole
serialized: byIndex # Add this
values:
- admin
- user
Use new default:
enum: UserRole
# serialized: byName - is now default
values:
- admin
- user
Model changesβ
SerializableEntity removedβ
The SerializableEntity class, deprecated since Serverpod 2.0, has been removed. Replace extends SerializableEntity with implements SerializableModel in your custom model classes.
Before:
class CustomClass extends SerializableEntity {
Map<String, dynamic> toJson() => {'name': name};
}
After:
class CustomClass implements SerializableModel {
Map<String, dynamic> toJson() => {'name': name};
factory CustomClass.fromJson(Map<String, dynamic> json) => CustomClass(json['name']);
}
Removed YAML keywordsβ
The following deprecated YAML keywords have been removed. Run serverpod generate to see errors with migration guidance.
| Old keyword | Replacement |
|---|---|
parent=table | relation(parent=table) |
database | scope=serverOnly |
api | !persist |
Before:
class: Company
table: company
fields:
name: String
ceoId: int, parent=employee
internalNotes: String, database
tempData: String, api
After:
class: Company
table: company
fields:
name: String
ceoId: int, relation(parent=employee)
internalNotes: String, scope=serverOnly
tempData: String, !persist
Deprecated APIsβ
The following APIs have been deprecated but will continue to work for the foreseeable future to maintain compatibility with older clients. They will be removed in a future major version:
- Legacy streaming endpoints β Use streaming methods for new code
Other changesβ
Additional improvements in Serverpod 3.0:
-
Session log user ID as String: The session log table now stores authenticated user IDs as
Stringinstead ofint. This enables support for non-numeric identifiers such as UUIDs. A database migration is required (see Create and apply migrations). -
Graceful SIGTERM shutdown: The server now handles
SIGTERMsignals gracefully, allowing in-flight requests to complete before shutting down. This improves behavior in containerized deployments (Docker, Kubernetes) where orchestrators sendSIGTERMbefore terminating processes. -
Auto-stop on integrity check failure: In development mode, the server now automatically stops if the database integrity check fails (e.g., schema mismatch). This prevents running with an inconsistent database state during development.