From a9daf1a858fe740d402cfee9d1283f571655f625 Mon Sep 17 00:00:00 2001 From: Preston Date: Tue, 8 Sep 2026 16:45:41 -0500 Subject: [PATCH 1/6] Import the Flutter client from client.dart. --- .../01-creating-endpoints.md | 6 +++--- .../03-working-with-the-database.md | 2 +- .../01-your-serverpod-project.md | 3 ++- .../01-working-with-endpoints.md | 16 +++++++++------- docs/07-tutorials/02-real-time-communication.md | 15 ++++++--------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/05-build-your-first-app/01-creating-endpoints.md b/docs/05-build-your-first-app/01-creating-endpoints.md index fe99dddb..05702bd0 100644 --- a/docs/05-build-your-first-app/01-creating-endpoints.md +++ b/docs/05-build-your-first-app/01-creating-endpoints.md @@ -126,7 +126,7 @@ Create `magic_recipe_flutter/lib/screens/recipe_screen.dart`: ```dart import 'package:flutter/material.dart'; -import '../main.dart'; +import '../client.dart'; import 'greetings_screen.dart'; class RecipeScreen extends StatefulWidget { @@ -206,7 +206,7 @@ class _RecipeScreenState extends State { } ``` -`client` comes from `main.dart`, where the template already wired it to talk to your server, and `ResultDisplay` is reused from `greetings_screen.dart`. +`client` comes from `client.dart`, where the template already wired it to talk to your server, and `ResultDisplay` is reused from `greetings_screen.dart`. Now show the recipe screen instead of the greeting demo. In `magic_recipe_flutter/lib/main.dart`, add the import: @@ -220,7 +220,7 @@ Then, in the `MyHomePage` widget, change the body from `GreetingsScreen` to `Rec body: const RecipeScreen(), ``` -Save. UI edits like this would normally hot reload, but adding the endpoint also changed the generated client. The app's `client` is created once in `main()`, which only re-runs on a restart, so the app needs a hot restart to pick up the new `client.recipe` endpoint. +Save. UI edits like this would normally hot reload, but adding the endpoint also changed the generated client. The app's `client` is created once by `initializeClient()` in `main()`, which only re-runs on a restart, so the app needs a hot restart to pick up the new `client.recipe` endpoint. In the `serverpod start` terminal: diff --git a/docs/05-build-your-first-app/03-working-with-the-database.md b/docs/05-build-your-first-app/03-working-with-the-database.md index 5113de20..6b202b04 100644 --- a/docs/05-build-your-first-app/03-working-with-the-database.md +++ b/docs/05-build-your-first-app/03-working-with-the-database.md @@ -87,7 +87,7 @@ Update `recipe_screen.dart` to load past recipes when it opens and list them nex import 'package:flutter/material.dart'; import 'package:magic_recipe_client/magic_recipe_client.dart'; -import '../main.dart'; +import '../client.dart'; import 'greetings_screen.dart'; class RecipeScreen extends StatefulWidget { diff --git a/docs/06-concepts/01-server-fundamentals/01-your-serverpod-project.md b/docs/06-concepts/01-server-fundamentals/01-your-serverpod-project.md index b2b131c3..0cc8ab84 100644 --- a/docs/06-concepts/01-server-fundamentals/01-your-serverpod-project.md +++ b/docs/06-concepts/01-server-fundamentals/01-your-serverpod-project.md @@ -43,7 +43,8 @@ myproject/ ├── myproject_client/ # Generated client package │ └── lib/src/protocol/ # Generated calls and models (do not edit) └── myproject_flutter/ # Your Flutter app - ├── lib/main.dart # App code: creates the global Client + ├── lib/main.dart # App entry point: initializes the client, then runs the app + ├── lib/client.dart # Creates the global Client that talks to the server ├── lib/screens/ # Scaffolded sign-in and greetings screens ├── lib/driver.dart # Entry point serverpod start uses to launch the app └── assets/config.json # The server URL the app reads at startup diff --git a/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md b/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md index ca29282e..66b586aa 100644 --- a/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md +++ b/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md @@ -38,19 +38,21 @@ Your app calls the method through the generated client: var result = await client.example.hello('World'); ``` -The scaffolded Flutter app already creates that client in `lib/main.dart`, connected to your development server: +The scaffolded Flutter app already creates that client in `lib/client.dart`, connected to your development server, and `main()` initializes it before running the app: ```dart +// lib/client.dart late final Client client; -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - final serverUrl = await getServerUrl(); - - client = Client(serverUrl) +Future initializeClient() async { + client = Client(await getServerUrl()) ..connectivityMonitor = FlutterConnectivityMonitor(); +} +// lib/main.dart +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await initializeClient(); runApp(const MyApp()); } ``` diff --git a/docs/07-tutorials/02-real-time-communication.md b/docs/07-tutorials/02-real-time-communication.md index 9ff229a3..f2d8ed9d 100644 --- a/docs/07-tutorials/02-real-time-communication.md +++ b/docs/07-tutorials/02-real-time-communication.md @@ -211,22 +211,19 @@ cd pixorama_flutter flutter pub add pixels ``` -Next, let's open the `main.dart` file and rename the `MyHomePage` class to `PixoramaApp`. We also remove the demo code and replace it with a `Scaffold` containing a `Pixorama` widget. This is our new main file: +Next, let's open the `main.dart` file. The template already creates the `client` in `lib/client.dart` and initializes it in `main()`, so we keep that part. Rename the `MyHomePage` class to `PixoramaApp`, remove the demo code, and replace it with a `Scaffold` containing a `Pixorama` widget. This is our new main file: ```dart // lib/main.dart -import 'package:pixorama_client/pixorama_client.dart'; import 'package:flutter/material.dart'; -import 'package:serverpod_flutter/serverpod_flutter.dart'; +import 'client.dart'; import 'src/pixorama.dart'; -var client = Client('http://$localhost:8080/') - ..connectivityMonitor = FlutterConnectivityMonitor(); - -void main() { - // Start the app. +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await initializeClient(); runApp(const PixoramaApp()); } @@ -257,7 +254,7 @@ import 'package:flutter/material.dart'; import 'package:pixels/pixels.dart'; import 'package:pixorama_client/pixorama_client.dart'; -import '../../main.dart'; +import '../client.dart'; class Pixorama extends StatefulWidget { const Pixorama({super.key}); From 338d993db52b8295fdcc182ceb18023bd5d56660 Mon Sep 17 00:00:00 2001 From: Preston Date: Tue, 8 Sep 2026 16:48:49 -0500 Subject: [PATCH 2/6] Point provider sign-in setup at client.dart. --- .../05-providers/03-google/01-setup.md | 8 +++--- .../03-google/02-customizations.md | 2 +- .../03-google/04-troubleshooting.md | 2 +- .../05-providers/04-apple/01-setup.md | 4 +-- .../05-providers/05-facebook/01-setup.md | 13 ++++----- .../05-providers/06-firebase/01-setup.md | 28 +++++++++++-------- .../05-providers/07-github/01-setup.md | 16 ++++------- .../03-migrate-from-legacy-auth.md | 2 +- 8 files changed, 36 insertions(+), 39 deletions(-) diff --git a/docs/06-concepts/04-authentication/05-providers/03-google/01-setup.md b/docs/06-concepts/04-authentication/05-providers/03-google/01-setup.md index 3b73ab4c..92945e73 100644 --- a/docs/06-concepts/04-authentication/05-providers/03-google/01-setup.md +++ b/docs/06-concepts/04-authentication/05-providers/03-google/01-setup.md @@ -303,10 +303,10 @@ The examples below use port `8082` (Serverpod's default from `config/development ### Initialize the Google sign-in service -In your Flutter app's `main.dart`, add `initializeGoogleSignIn()` right after the existing `client.auth.initialize()` call: +In your Flutter app's `lib/client.dart`, the template creates the `Client` and calls `client.auth.initialize()` inside `initializeClient()`. Add `initializeGoogleSignIn()` right after that call: ```dart -client.auth.initialize(); +unawaited(client.auth.initialize()); client.auth.initializeGoogleSignIn(); ``` @@ -328,7 +328,7 @@ if (kIsWeb) { Swap the redirect URI for your production URL when deploying. See [Configuring the web redirect URI](./customizations#configuring-the-web-redirect-uri) to avoid hard-coding it per environment. :::warning -On web, the app Serverpod serves is the build you created in [Web setup](#web). After changing `main.dart` (for example the `redirectUri`), run the build command again and hard-reload the browser. A stale build keeps sending the old values, and sign-in fails with [redirect_uri_mismatch](./troubleshooting#sign-in-fails-with-redirect_uri_mismatch). +On web, the app Serverpod serves is the build you created in [Web setup](#web). After changing `client.dart` (for example the `redirectUri`), run the build command again and hard-reload the browser. A stale build keeps sending the old values, and sign-in fails with [redirect_uri_mismatch](./troubleshooting#sign-in-fails-with-redirect_uri_mismatch). ::: ### Show the Google sign-in button @@ -406,7 +406,7 @@ body: SignInScreen( ``` :::warning -The `initializeGoogleSignIn` call lives in `main()`, and hot reload does not re-run `main()`. After making these changes, hot restart the app: press **R** in the `serverpod start` terminal, or rerun `flutter run`. Until then, the Google button stays hidden. +The `initializeGoogleSignIn` call lives in `initializeClient()`, which `main()` runs once, and hot reload does not re-run it. After making these changes, hot restart the app: press **R** in the `serverpod start` terminal, or rerun `flutter run`. Until then, the Google button stays hidden. ::: The `SignInWidget` renders the standard Google sign-in button: diff --git a/docs/06-concepts/04-authentication/05-providers/03-google/02-customizations.md b/docs/06-concepts/04-authentication/05-providers/03-google/02-customizations.md index a14ac080..baa90cf7 100644 --- a/docs/06-concepts/04-authentication/05-providers/03-google/02-customizations.md +++ b/docs/06-concepts/04-authentication/05-providers/03-google/02-customizations.md @@ -213,7 +213,7 @@ You can also set these environment variables in your IDE's run configuration or ### Configuring the web redirect URI -You can pass the web redirect URI to `initializeGoogleSignIn` via `--dart-define`. This is useful when building for different environments (development, staging, production) without changing `main.dart`: +You can pass the web redirect URI to `initializeGoogleSignIn` via `--dart-define`. This is useful when building for different environments (development, staging, production) without changing `client.dart`: ```dart if (kIsWeb) { diff --git a/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md b/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md index 817aa773..e437ccd0 100644 --- a/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md +++ b/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md @@ -197,7 +197,7 @@ On the Android emulator, `10.0.2.2` maps to the host machine. On a physical devi **Cause:** The `SignInWidget` shows the Google button when the client has a registered `GoogleIdpEndpoint` and the Google sign-in service is initialized. The common misses: -- The app was hot reloaded after adding `initializeGoogleSignIn` to `main.dart`. Hot reload does not re-run `main()`, so the service is never initialized. +- The app was hot reloaded after adding `initializeGoogleSignIn` to `client.dart`. Hot reload does not re-run `initializeClient()`, so the service is never initialized. - `GoogleIdpEndpoint` is missing on the server, or the client was not regenerated after adding it. - On web, `initializeGoogleSignIn` was called without `clientId` and `redirectUri`. The widget renders nothing without them. diff --git a/docs/06-concepts/04-authentication/05-providers/04-apple/01-setup.md b/docs/06-concepts/04-authentication/05-providers/04-apple/01-setup.md index 067a136b..c1ee6fd7 100644 --- a/docs/06-concepts/04-authentication/05-providers/04-apple/01-setup.md +++ b/docs/06-concepts/04-authentication/05-providers/04-apple/01-setup.md @@ -249,10 +249,10 @@ The redirect URI and `appleWebRedirectUri` were already configured in the [Store ### Initialize the Sign in with Apple service -In your Flutter app's `main.dart` file (e.g., `my_project_flutter/lib/main.dart`), the template already sets up the `Client` and calls `client.auth.initialize()`. Add `client.auth.initializeAppleSignIn()` right after it: +In your Flutter app's `lib/client.dart`, the template already sets up the `Client` and calls `client.auth.initialize()` inside `initializeClient()`. Add `client.auth.initializeAppleSignIn()` right after it: ```dart -client.auth.initialize(); +unawaited(client.auth.initialize()); client.auth.initializeAppleSignIn(); ``` diff --git a/docs/06-concepts/04-authentication/05-providers/05-facebook/01-setup.md b/docs/06-concepts/04-authentication/05-providers/05-facebook/01-setup.md index 85b7d700..2f8afdf7 100644 --- a/docs/06-concepts/04-authentication/05-providers/05-facebook/01-setup.md +++ b/docs/06-concepts/04-authentication/05-providers/05-facebook/01-setup.md @@ -456,18 +456,17 @@ For more detailed macOS setup instructions, refer to the [flutter_facebook_auth ### Initialize the Facebook sign-in service -Initialize the service in your app's `main()` function using the `initializeFacebookSignIn()` extension method on `FlutterAuthSessionManager`, on the line after `client.auth.initialize()`. +Initialize the service in your app's `lib/client.dart` using the `initializeFacebookSignIn()` extension method on `FlutterAuthSessionManager`, on the line after the existing `client.auth.initialize()` call in `initializeClient()`. ```dart -import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart'; import 'package:serverpod_auth_idp_flutter_facebook/serverpod_auth_idp_flutter_facebook.dart'; -import 'package:your_client/your_client.dart'; -final client = Client('http://localhost:8080/') - ..authSessionManager = FlutterAuthSessionManager(); +Future initializeClient() async { + client = Client(await serverUrl) + ..connectivityMonitor = FlutterConnectivityMonitor() + ..authSessionManager = FlutterAuthSessionManager(); -void main() { - client.auth.initialize(); + unawaited(client.auth.initialize()); client.auth.initializeFacebookSignIn(); } ``` diff --git a/docs/06-concepts/04-authentication/05-providers/06-firebase/01-setup.md b/docs/06-concepts/04-authentication/05-providers/06-firebase/01-setup.md index 10d6a8d6..6c55f734 100644 --- a/docs/06-concepts/04-authentication/05-providers/06-firebase/01-setup.md +++ b/docs/06-concepts/04-authentication/05-providers/06-firebase/01-setup.md @@ -193,18 +193,14 @@ If your Flutter project folder name contains an underscore (or any character tha ### 3. Initialize Firebase and Serverpod -In your Flutter app's `main.dart` file (e.g., `my_project_flutter/lib/main.dart`), the template already sets up the `Client`. Initialize both Firebase and the Serverpod auth services: +The template creates the `Client` in `lib/client.dart` and initializes it from `main()`. Initialize Firebase before the client, in `lib/main.dart`: ```dart import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; -import 'package:serverpod_flutter/serverpod_flutter.dart'; -import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart'; -import 'package:serverpod_auth_idp_flutter_firebase/serverpod_auth_idp_flutter_firebase.dart'; -import 'package:your_client/your_client.dart'; -import 'firebase_options.dart'; -late Client client; +import 'client.dart'; +import 'firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -213,15 +209,23 @@ void main() async { options: DefaultFirebaseOptions.currentPlatform, ); - client = Client('http://localhost:8080/') + await initializeClient(); + runApp(const MyApp()); +} +``` + +Then, in `lib/client.dart`, add `initializeFirebaseSignIn()` right after the existing `client.auth.initialize()` call in `initializeClient()`: + +```dart +import 'package:serverpod_auth_idp_flutter_firebase/serverpod_auth_idp_flutter_firebase.dart'; + +Future initializeClient() async { + client = Client(await serverUrl) ..connectivityMonitor = FlutterConnectivityMonitor() ..authSessionManager = FlutterAuthSessionManager(); - await client.auth.initialize(); - + unawaited(client.auth.initialize()); client.auth.initializeFirebaseSignIn(); - - runApp(const MyApp()); } ``` diff --git a/docs/06-concepts/04-authentication/05-providers/07-github/01-setup.md b/docs/06-concepts/04-authentication/05-providers/07-github/01-setup.md index 4d7a2651..b2efdaae 100644 --- a/docs/06-concepts/04-authentication/05-providers/07-github/01-setup.md +++ b/docs/06-concepts/04-authentication/05-providers/07-github/01-setup.md @@ -244,34 +244,28 @@ The examples below use port `8082` (Serverpod's default from `config/development ### Initialize the GitHub sign-in service -Open your Flutter app's `main.dart` (e.g., `my_project_flutter/lib/main.dart`). The Serverpod template already creates the `Client` and calls `client.auth.initialize()` inside `main()`. Add `client.auth.initializeGitHubSignIn(...)` on the line immediately after it. +Open your Flutter app's `lib/client.dart`. The Serverpod template already creates the `Client` and calls `client.auth.initialize()` inside `initializeClient()`. Add `client.auth.initializeGitHubSignIn(...)` on the line immediately after it. The GitHub provider requires `clientId` and `redirectUri` on every platform because GitHub does not have native platform-specific clients (unlike Google or Apple): ```dart -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - final serverUrl = await getServerUrl(); - - client = Client(serverUrl) +Future initializeClient() async { + client = Client(await serverUrl) ..connectivityMonitor = FlutterConnectivityMonitor() ..authSessionManager = FlutterAuthSessionManager(); - await client.auth.initialize(); + unawaited(client.auth.initialize()); await client.auth.initializeGitHubSignIn( clientId: 'your-github-client-id', redirectUri: 'com.example.yourapp://auth', ); - - runApp(const MyApp()); } ``` Replace `your-github-client-id` with the **Client ID** from your GitHub App, and `redirectUri` with the matching callback URL you registered: a reverse-DNS custom scheme for mobile, or the route URL from [Web](#web) for Flutter web. Swap the redirect URI for your production URL when deploying. :::tip -To keep these values out of `main.dart` and vary them per build, read them from `--dart-define`. See [Configuring client IDs on the app](./customizations#configuring-client-ids-on-the-app) for the pattern. +To keep these values out of `client.dart` and vary them per build, read them from `--dart-define`. See [Configuring client IDs on the app](./customizations#configuring-client-ids-on-the-app) for the pattern. ::: ### Show the GitHub sign-in button diff --git a/docs/11-upgrading/03-migrate-from-legacy-auth.md b/docs/11-upgrading/03-migrate-from-legacy-auth.md index db6e99cf..b46bb28a 100644 --- a/docs/11-upgrading/03-migrate-from-legacy-auth.md +++ b/docs/11-upgrading/03-migrate-from-legacy-auth.md @@ -227,7 +227,7 @@ A migrated user can now sign in with their old password or Google account and la ## Update the Flutter app -In `_flutter/lib/main.dart`, swap the auth setup to use `FlutterAuthSessionManager` and call `initAndImportLegacySessionIfNeeded` before any sign-in UI renders. This exchanges any old auth key stored on the device for a new modular session so existing installs do not have to sign in again. +Where your app creates the `Client` (`lib/client.dart` in projects created with 4.0 or upgraded with `serverpod create .`, otherwise `lib/main.dart`), swap the auth setup to use `FlutterAuthSessionManager` and call `initAndImportLegacySessionIfNeeded` before any sign-in UI renders. This exchanges any old auth key stored on the device for a new modular session so existing installs do not have to sign in again. ```dart import 'package:serverpod_auth_bridge_flutter/serverpod_auth_bridge_flutter.dart'; From 1c13c689e71f4c952734173e689a3fd7042eb8ef Mon Sep 17 00:00:00 2001 From: Preston Date: Tue, 8 Sep 2026 16:50:11 -0500 Subject: [PATCH 3/6] Update the client exception example for the 4.0 hierarchy. --- .../03-error-handling-and-exceptions.md | 20 +++++++++---------- .../03-google/04-troubleshooting.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md b/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md index f53d63df..52730dd0 100644 --- a/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md +++ b/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md @@ -197,8 +197,10 @@ fields: A call from the client can fail in three ways, and you usually handle each one differently: - A **serializable exception you defined** (`MyException` above): a known, app-level failure. Catch it by its type and show the user what happened. (It is sent as an HTTP 400 response with a typed payload.) -- A **`ServerpodClientException`**: something went wrong in the communication or on the server. Its typed subclasses map to HTTP status codes: `ServerpodClientBadRequest` (400), `ServerpodClientUnauthorized` (401), `ServerpodClientForbidden` (403), `ServerpodClientNotFound` (404), and `ServerpodClientInternalServerError` (500). -- A **connection failure**: when the app cannot reach the server (offline, wrong URL, or a timeout), it throws a `ServerpodClientException` with a `statusCode` of `-1`. A call that exceeds the [request size limit](../endpoints-and-apis#pass-and-return-data) fails with a generic `ServerpodClientException` with status code 413. +- A **`ServerpodClientHttpException`**: the server answered with an error status, available as `statusCode`. Its subclasses map to HTTP status codes: `ServerpodClientBadRequest` (400), `ServerpodClientUnauthorized` (401), `ServerpodClientForbidden` (403), `ServerpodClientNotFound` (404), and `ServerpodClientInternalServerError` (500). Any other status is a `ServerpodClientUnknownHttpException`, such as the 413 returned when a call exceeds the [request size limit](../endpoints-and-apis#pass-and-return-data). +- A **`ServerpodClientNetworkException`**: the app cannot reach the server (offline, wrong URL, or a timeout). There is no status code. + +Both extend the sealed `ServerpodClientException`, so `on ServerpodClientException` still catches every client-side failure at once. Calls to [streaming methods](./streaming) fail with their own connection-level exception family; see [error handling in streams](./streaming#error-handling). @@ -213,14 +215,12 @@ try { } on ServerpodClientUnauthorized catch (_) { // The call requires the user to sign in. redirectToSignIn(); -} on ServerpodClientException catch (e) { - if (e.statusCode == -1) { - // Could not reach the server. - showError('Cannot reach the server. Check your connection and try again.'); - } else { - // The server returned an error, for example a 500. - showError('Something went wrong. Please try again.'); - } +} on ServerpodClientNetworkException catch (_) { + // Could not reach the server. + showError('Cannot reach the server. Check your connection and try again.'); +} on ServerpodClientHttpException catch (e) { + // The server returned an error, for example a 500. + showError('Something went wrong (${e.statusCode}). Please try again.'); } ``` diff --git a/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md b/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md index e437ccd0..de4e80cc 100644 --- a/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md +++ b/docs/06-concepts/04-authentication/05-providers/03-google/04-troubleshooting.md @@ -171,7 +171,7 @@ For Firebase-based projects using the Gradle plugin, make sure a Web application ## Endpoint calls fail on Android with connection refused -**Problem:** Sign-in completes at Google, but the app then fails with `ServerpodClientException: ... Connection refused ... uri=http://localhost:8080/...`. +**Problem:** Sign-in completes at Google, but the app then fails with `ServerpodClientNetworkException: ... Connection refused ... uri=http://localhost:8080/...`. **Cause:** On Android, `localhost` is the emulator or device itself, not the machine running your server. The project template's `assets/config.json` sets `apiUrl` to `http://localhost:8080`, and that value takes precedence over the framework's platform-aware default (see [server URL resolution](../../../endpoints-and-apis)). From bf9ed14c3d86628640ce62e468b3308343321b63 Mon Sep 17 00:00:00 2001 From: Preston Date: Tue, 8 Sep 2026 16:52:38 -0500 Subject: [PATCH 4/6] Replace the removed --mini flag in the real-time tutorial. --- .../cli/commands/create/_create.md | 2 +- .../02-real-time-communication.md | 26 ++++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/06-concepts/cli/commands/create/_create.md b/docs/06-concepts/cli/commands/create/_create.md index 320d380d..db16d2bb 100644 --- a/docs/06-concepts/cli/commands/create/_create.md +++ b/docs/06-concepts/cli/commands/create/_create.md @@ -2,4 +2,4 @@ `serverpod create` scaffolds a new Serverpod project. By default it generates a full server project with a database, a server package, a client package, and a Flutter app. -Pass `--template mini` for a minimal project without a database, or `--template module` to create a shareable module. To set up the prerequisites first, see [Installation](../../../../04-get-started/01-installation.md). +Pass `--template server` for a server without a Flutter app, `--template module` to create a shareable module, or `--no-database` for a project without a database. To set up the prerequisites first, see [Installation](../../../../04-get-started/01-installation.md). diff --git a/docs/07-tutorials/02-real-time-communication.md b/docs/07-tutorials/02-real-time-communication.md index f2d8ed9d..176d6313 100644 --- a/docs/07-tutorials/02-real-time-communication.md +++ b/docs/07-tutorials/02-real-time-communication.md @@ -8,7 +8,7 @@ _This tutorial is also available as a video._ :::info -Before you begin, make sure that you have [installed Serverpod](/). It's also recommended that you read the [Get started with Mini](../../serverpod-mini) guide. +Before you begin, make sure that you have [installed Serverpod](../installation). ::: @@ -28,17 +28,19 @@ With the release of Serverpod 2.1, a new feature called [streaming methods](../c ## Setting up the project -We begin by creating a new project with the `serverpod create` command. Since we don't need to store data in a database, we'll use the Mini version of Serverpod. Serverpod Mini is a lightweight version of Serverpod without a database, advanced logging, and other features - perfect for our needs. Create the project with the command: +We begin by creating a new project with the `serverpod create` command. Pixorama keeps its image in memory, so it doesn't need a database. In the interactive setup, deselect **Database** under **Database & caching** and keep the other defaults: ```bash -serverpod create pixorama --mini +serverpod create pixorama ``` +If you run the command non-interactively, pass `--no-interactive --no-database` instead. + Now, let's open the project in VS Code and explore the structure. The server code resides in the `pixorama_server` package. We'll start by creating models - classes that we can serialize and pass between the client and server. Our models will be placed in the `lib/src/models` directory. ## Creating models -First, we remove the `example.spy.yaml` model, as we won't need it. We'll create two new models: `ImageData` and `ImageUpdate`. Place them in the `lib/src/models` directory and call them `image_data.spy.yaml` and `image_update.spy.yaml`. +First, we remove the template's example feature, the `lib/src/greetings` directory in `pixorama_server`, as we won't need it. We'll create two new models: `ImageData` and `ImageUpdate`. Place them in the `lib/src/models` directory and call them `image_data.spy.yaml` and `image_update.spy.yaml`. ```yaml # lib/src/models/image_data.spy.yaml @@ -63,11 +65,10 @@ fields: The `ImageUpdate` model captures changes to individual pixels, including the pixel's index in the byte array and its new color value. -With our models defined, we run serverpod generate to create the actual Dart files for these models. Run the command from your server's root directory (`pixorama_server`). +With our models defined, start the project so Serverpod generates the Dart classes for them. Run the command from the project root and leave it running: it regenerates code and hot reloads the server every time you save a file. ```bash -cd pixorama_server -serverpod generate +serverpod start ``` ## Building the server @@ -193,18 +194,13 @@ class PixoramaEndpoint extends Endpoint { } ``` -That's all the code we need to write for the server side. To make the new endpoint available to our Flutter app, we run serverpod generate in the root directory of our server. - -```bash -cd pixorama_server -serverpod generate -``` +That's all the code we need to write for the server side. With `serverpod start` running, the new endpoint is generated into the client package as soon as you save. If you're not running it, run `serverpod generate` in `pixorama_server` instead. ## Building the Flutter app With the server side complete, it's time to build the Flutter app. When we created the project, Serverpod set up a basic Flutter app for us in the `pixorama_flutter` package. -First, we will use the pixels package to draw our pixel editor. Import it by running the following command in your `pixorama_flutter` directory: +Since we removed the greeting endpoint, delete `lib/screens/greetings_screen.dart`, which used it. Then add the pixels package to draw our pixel editor. Import it by running the following command in your `pixorama_flutter` directory: ```bash cd pixorama_flutter @@ -378,7 +374,7 @@ class _PixoramaState extends State { ## Running Pixorama -To test Pixorama, start the server and the app from the `pixorama_server` directory: +If you kept `serverpod start` running, the server and the app have already picked up your changes. Otherwise, start them from the project root: ```bash serverpod start From c67ea0f65b310be8eb9fa99c47055c7615e2b6b2 Mon Sep 17 00:00:00 2001 From: Preston Date: Thu, 10 Sep 2026 10:21:59 -0500 Subject: [PATCH 5/6] Apply review feedback on the client exception example and client snippet. --- .../02-endpoints-and-apis/01-working-with-endpoints.md | 4 +++- .../03-error-handling-and-exceptions.md | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md b/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md index 66b586aa..816f291d 100644 --- a/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md +++ b/docs/06-concepts/02-endpoints-and-apis/01-working-with-endpoints.md @@ -42,10 +42,12 @@ The scaffolded Flutter app already creates that client in `lib/client.dart`, con ```dart // lib/client.dart +final serverUrl = getServerUrl(); + late final Client client; Future initializeClient() async { - client = Client(await getServerUrl()) + client = Client(await serverUrl) ..connectivityMonitor = FlutterConnectivityMonitor(); } diff --git a/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md b/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md index 52730dd0..def2d08a 100644 --- a/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md +++ b/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md @@ -194,13 +194,13 @@ fields: ## Handle errors in your app -A call from the client can fail in three ways, and you usually handle each one differently: +A call from the client can fail in a few ways, and you usually handle each one differently: - A **serializable exception you defined** (`MyException` above): a known, app-level failure. Catch it by its type and show the user what happened. (It is sent as an HTTP 400 response with a typed payload.) - A **`ServerpodClientHttpException`**: the server answered with an error status, available as `statusCode`. Its subclasses map to HTTP status codes: `ServerpodClientBadRequest` (400), `ServerpodClientUnauthorized` (401), `ServerpodClientForbidden` (403), `ServerpodClientNotFound` (404), and `ServerpodClientInternalServerError` (500). Any other status is a `ServerpodClientUnknownHttpException`, such as the 413 returned when a call exceeds the [request size limit](../endpoints-and-apis#pass-and-return-data). - A **`ServerpodClientNetworkException`**: the app cannot reach the server (offline, wrong URL, or a timeout). There is no status code. -Both extend the sealed `ServerpodClientException`, so `on ServerpodClientException` still catches every client-side failure at once. +All of them extend the sealed `ServerpodClientException`, so `on ServerpodClientException` still catches every client-side failure at once. Calls to [streaming methods](./streaming) fail with their own connection-level exception family; see [error handling in streams](./streaming#error-handling). @@ -221,6 +221,9 @@ try { } on ServerpodClientHttpException catch (e) { // The server returned an error, for example a 500. showError('Something went wrong (${e.statusCode}). Please try again.'); +} on ServerpodClientException catch (_) { + // Anything else the client could not classify. + showError('Something went wrong. Please try again.'); } ``` From e0d34ccd685f27c33867ce29d300277d52157000 Mon Sep 17 00:00:00 2001 From: Preston Date: Thu, 10 Sep 2026 13:37:47 -0500 Subject: [PATCH 6/6] Clarify which exceptions the client-side catch-all covers. --- .../02-endpoints-and-apis/03-error-handling-and-exceptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md b/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md index def2d08a..c7f7339f 100644 --- a/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md +++ b/docs/06-concepts/02-endpoints-and-apis/03-error-handling-and-exceptions.md @@ -200,7 +200,7 @@ A call from the client can fail in a few ways, and you usually handle each one d - A **`ServerpodClientHttpException`**: the server answered with an error status, available as `statusCode`. Its subclasses map to HTTP status codes: `ServerpodClientBadRequest` (400), `ServerpodClientUnauthorized` (401), `ServerpodClientForbidden` (403), `ServerpodClientNotFound` (404), and `ServerpodClientInternalServerError` (500). Any other status is a `ServerpodClientUnknownHttpException`, such as the 413 returned when a call exceeds the [request size limit](../endpoints-and-apis#pass-and-return-data). - A **`ServerpodClientNetworkException`**: the app cannot reach the server (offline, wrong URL, or a timeout). There is no status code. -All of them extend the sealed `ServerpodClientException`, so `on ServerpodClientException` still catches every client-side failure at once. +Both extend the sealed `ServerpodClientException`, along with `ServerpodClientUnknownException` for failures the client cannot classify. Catching `ServerpodClientException` handles all of them at once. Calls to [streaming methods](./streaming) fail with their own connection-level exception family; see [error handling in streams](./streaming#error-handling).