Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/05-build-your-first-app/01-creating-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -206,7 +206,7 @@ class _RecipeScreenState extends State<RecipeScreen> {
}
```

`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:

Expand All @@ -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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> initializeClient() async {
client = Client(await getServerUrl())
Comment thread
FXschwartz marked this conversation as resolved.
Outdated
..connectivityMonitor = FlutterConnectivityMonitor();
}

// lib/main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeClient();
runApp(const MyApp());
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@ fields:
A call from the client can fail in three ways, and you usually handle each one differently:
Comment thread
FXschwartz marked this conversation as resolved.
Outdated

- 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).

Expand All @@ -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.');
}
Comment thread
FXschwartz marked this conversation as resolved.
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
```

Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)).

Expand All @@ -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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> initializeClient() async {
client = Client(await serverUrl)
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();

void main() {
client.auth.initialize();
unawaited(client.auth.initialize());
client.auth.initializeFacebookSignIn();
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<void> initializeClient() async {
client = Client(await serverUrl)
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();

await client.auth.initialize();

unawaited(client.auth.initialize());
client.auth.initializeFirebaseSignIn();

runApp(const MyApp());
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> 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
Expand Down
2 changes: 1 addition & 1 deletion docs/06-concepts/cli/commands/create/_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Loading
Loading