Skip to content

Commit 60473d0

Browse files
authored
fix: Copy new sessions doc to latest versioned doc (serverpod#321)
1 parent af880ce commit 60473d0

File tree

1 file changed

+288
-11
lines changed

1 file changed

+288
-11
lines changed
Lines changed: 288 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,303 @@
11
# Sessions
22

3-
The `Session` object provides information about the current context in a method call in Serverpod. It provides access to the database, caching, authentication, data storage, and messaging within the server. It will also contain information about the HTTP request object.
3+
A Session in Serverpod is a request-scoped context object that exists for the duration of a single client request or connection. It provides access to server resources and maintains state during request processing.
44

5-
If you need additional information about a call, you may need to cast the Session to one of its subclasses, e.g., `MethodCallSession` or `StreamingSession`. The `MethodCallSession` object provides additional properties, such as the name of the endpoint and method and the underlying `HttpRequest` object.
5+
Sessions are the gateway to Serverpod's functionality - every interaction with the database, cache, file storage, or messaging system happens through a session. The framework automatically creates the appropriate session type when a client makes a request, manages its lifecycle, and ensures proper cleanup when the request completes. For special cases like background tasks or system operations, you can also create and manage sessions manually.
66

7-
:::tip
7+
:::note
8+
9+
A Serverpod Session should not be confused with the concept of "web sessions" or "user sessions" which persist over multiple API calls. See the [Authentication documentation](./11-authentication/01-setup.md) for managing persistent authentication.
10+
11+
:::
12+
13+
## Quick reference
14+
15+
### Essential properties
16+
17+
- **`db`** - Database access. [See database docs](./06-database/01-connection.md)
18+
- **`caches`** - Local and distributed caching. [See caching docs](./08-caching.md)
19+
- **`storage`** - File storage operations. [See file uploads](./12-file-uploads.md)
20+
- **`messages`** - Server events for real-time communication within and across servers. [See server events docs](./16-server-events.md)
21+
- **`passwords`** - Credentials from config and environment. [See configuration](./07-configuration.md)
22+
- **`authenticated`** - Current user authentication info. [See authentication docs](./11-authentication/02-basics.md)
23+
24+
### Key methods
25+
26+
- **`log(message, level)`** - Add log entry
27+
- **`addWillCloseListener(callback)`** - Register cleanup callback
28+
29+
## Session types
30+
31+
Serverpod creates different session types based on the context:
32+
33+
| Type | Created For | Lifetime | Common Use Cases |
34+
| ----------------------- | ---------------------------- | ------------------- | ---------------------------- |
35+
| **MethodCallSession** | `Future<T>` endpoint methods | Single request | API calls, CRUD operations |
36+
| **WebCallSession** | Web server routes | Single request | Web pages, form submissions |
37+
| **MethodStreamSession** | `Stream<T>` endpoint methods | Stream duration | Real-time updates, chat |
38+
| **StreamingSession** | WebSocket connections | Connection duration | Live dashboards, multiplayer |
39+
| **FutureCallSession** | Scheduled tasks | Task execution | Email sending, batch jobs |
40+
| **InternalSession** | Manual creation | Until closed | Background work, migrations |
841

9-
You can use the Session object to access the IP address of the client calling a method. Serverpod includes an extension on `HttpRequest` that allows you to access the IP address even if your server is running behind a load balancer.
42+
### Example: Automatic session (MethodCallSession)
1043

1144
```dart
12-
session as MethodCallSession;
13-
var ipAddress = session.httpRequest.remoteIpAddress;
45+
// lib/src/endpoints/example_endpoint.dart
46+
class ExampleEndpoint extends Endpoint {
47+
Future<String> hello(Session session, String name) async {
48+
// MethodCallSession is created automatically
49+
return 'Hello $name';
50+
// Session closes automatically when method returns
51+
}
52+
}
1453
```
1554

16-
:::
55+
### Example: Manual session (InternalSession)
56+
57+
InternalSession is the only session type that requires manual management:
58+
59+
```dart
60+
Future<void> performMaintenance() async {
61+
var session = await Serverpod.instance.createSession();
62+
try {
63+
// Perform operations
64+
await cleanupOldRecords(session);
65+
await updateStatistics(session);
66+
} finally {
67+
await session.close(); // Must close manually!
68+
}
69+
}
70+
```
1771

18-
## Creating sessions
72+
**Important**: Always use try-finally with InternalSession to prevent memory leaks.
1973

20-
In most cases, Serverpod manages the life cycle of the Session objects for you. A session is created for a call or a streaming connection and is disposed of when the call has been completed. In rare cases, you may want to create a session manually. For instance, if you are making a database call outside the scope of a method or a future call. In these cases, you can create a new session with the `createSession` method of the `Serverpod` singleton. You can access the singleton by the static `Serverpod.instance` field. If you create a new session, you are also responsible for closing it using the `session.close()` method.
74+
## Session lifecycle
2175

22-
:::note
76+
```mermaid
77+
flowchart TB
78+
Request([Request/Trigger]) --> Create[Session Created]
79+
Create --> Init[Initialize]
80+
Init --> Active[Execute Method]
81+
Active --> Close[Close Session]
82+
Close --> End([Request Complete])
83+
```
84+
85+
Sessions follow a predictable lifecycle from creation to cleanup. When a client makes a request, Serverpod automatically creates the appropriate session type (see table above), initializes it with a unique ID, and sets up access to resources like the database, cache, and file storage.
86+
87+
During the active phase, your operation executes with full access to Serverpod resources through the session. You can query the database, write logs, send messages, and access storage - all operations are tracked and tied to this specific session. When the operation completes, most sessions close automatically, writing any accumulated logs to the database and releasing all resources.
88+
89+
### Internal Sessions
90+
91+
The only exception is `InternalSession`, which you create manually for background tasks. Manual sessions require explicit closure with `session.close()`. Forgetting to close these sessions causes memory leaks as logs accumulate indefinitely. Always use try-finally blocks to ensure proper cleanup. After any session closes, attempting to use it throws a `StateError`.
2392

24-
It's not recommended to keep a session open indefinitely as it can lead to memory leaks, as the session stores logs until it is closed. It's inexpensive to create a new session, so keep them short.
93+
### Session cleanup callbacks
94+
95+
You can register callbacks that execute just before a session closes using `addWillCloseListener`. This is useful for cleanup operations, releasing resources, or performing final operations:
96+
97+
```dart
98+
Future<void> processData(Session session) async {
99+
var tempFile = File('/tmp/processing_data.tmp');
100+
101+
// Register cleanup callback
102+
session.addWillCloseListener((session) async {
103+
if (await tempFile.exists()) {
104+
await tempFile.delete();
105+
session.log('Cleaned up temporary file');
106+
}
107+
});
108+
109+
// Process data using temp file
110+
await tempFile.writeAsString('processing...');
111+
// Session closes automatically, cleanup callback runs
112+
}
113+
```
114+
115+
Cleanup callbacks run in the order they were registered and are called for all session types, including manual sessions when you call `session.close()`.
116+
117+
## Logging
118+
119+
Serverpod batches log entries for performance. During normal operations, logs accumulate in memory and are written to the database in a single batch when the session closes. This includes all your `session.log()` calls, database query timings, and session metadata. The exception is streaming sessions (`MethodStreamSession` and `StreamingSession`), which write logs continuously by default to avoid memory buildup during long connections.
120+
121+
:::warning
122+
123+
If you forget to close a manual session (`InternalSession`), logs remain in memory indefinitely and are never persisted - this is a common cause of both memory leaks and missing debug information.
25124

26125
:::
126+
127+
## Common pitfalls and solutions
128+
129+
### Pitfall 1: Using session after method returns
130+
131+
**Problem:** Using a session after it's closed throws a `StateError`
132+
133+
```dart
134+
Future<void> processUser(Session session, int userId) async {
135+
var user = await User.db.findById(session, userId);
136+
137+
// Schedule async work
138+
Timer(Duration(seconds: 5), () async {
139+
// ❌ Session is already closed!
140+
// This will throw: StateError: Session is closed
141+
await user.updateLastSeen(session);
142+
});
143+
144+
return; // Session closes here
145+
}
146+
```
147+
148+
**Solution 1 - Use FutureCalls:**
149+
150+
```dart
151+
Future<void> processUser(Session session, int userId) async {
152+
var user = await User.db.findById(session, userId);
153+
154+
// Schedule through Serverpod
155+
await session.serverpod.futureCallWithDelay(
156+
'updateLastSeen',
157+
UserIdData(userId: userId),
158+
Duration(seconds: 5),
159+
);
160+
161+
return;
162+
}
163+
```
164+
165+
**Solution 2 - Create manual session:**
166+
167+
```dart
168+
Future<void> processUser(Session session, int userId) async {
169+
var user = await User.db.findById(session, userId);
170+
171+
Timer(Duration(seconds: 5), () async {
172+
// Create new session for async work
173+
var newSession = await Serverpod.instance.createSession();
174+
try {
175+
await user.updateLastSeen(newSession);
176+
} finally {
177+
await newSession.close();
178+
}
179+
});
180+
181+
return;
182+
}
183+
```
184+
185+
### Pitfall 2: Forgetting to close manual sessions
186+
187+
**Problem:**
188+
189+
```dart
190+
// ❌ Memory leak!
191+
var session = await Serverpod.instance.createSession();
192+
var users = await User.db.find(session);
193+
// Forgot to close - session leaks memory
194+
```
195+
196+
**Solution - Always use try-finally:**
197+
198+
```dart
199+
var session = await Serverpod.instance.createSession();
200+
try {
201+
var users = await User.db.find(session);
202+
// Process users
203+
} finally {
204+
await session.close(); // Always runs
205+
}
206+
```
207+
208+
## Best practices
209+
210+
### 1. Let Serverpod manage sessions when possible
211+
212+
Prefer using the session provided to your endpoint rather than creating new ones:
213+
214+
```dart
215+
// ✅ Good - Use provided session
216+
Future<List<User>> getActiveUsers(Session session) async {
217+
return await User.db.find(
218+
session,
219+
where: (t) => t.isActive.equals(true),
220+
);
221+
}
222+
223+
// ❌ Avoid - Creating unnecessary session
224+
Future<List<User>> getActiveUsers(Session session) async {
225+
var newSession = await Serverpod.instance.createSession();
226+
try {
227+
return await User.db.find(newSession, ...);
228+
} finally {
229+
await newSession.close();
230+
}
231+
}
232+
```
233+
234+
### 2. Use FutureCalls for delayed operations
235+
236+
Instead of managing sessions for async work, use Serverpod's future call system:
237+
238+
```dart
239+
// ✅ Good - Let Serverpod manage the session
240+
await serverpod.futureCallWithDelay(
241+
'processPayment',
242+
PaymentData(orderId: order.id),
243+
Duration(hours: 1),
244+
);
245+
246+
// ❌ Complex - Manual session management
247+
Future.delayed(Duration(hours: 1), () async {
248+
var session = await Serverpod.instance.createSession();
249+
try {
250+
await processPayment(session, order.id);
251+
} finally {
252+
await session.close();
253+
}
254+
});
255+
```
256+
257+
### 3. Handle errors properly
258+
259+
Always handle exceptions to prevent unclosed sessions:
260+
261+
```dart
262+
// ✅ Good - Errors won't prevent session cleanup
263+
Future<void> safeOperation() async {
264+
var session = await Serverpod.instance.createSession();
265+
try {
266+
await riskyOperation(session);
267+
} catch (e) {
268+
session.log('Operation failed: $e', level: LogLevel.error);
269+
// Handle error appropriately
270+
} finally {
271+
await session.close();
272+
}
273+
}
274+
```
275+
276+
## Testing
277+
278+
When testing endpoints, the `TestSessionBuilder` can be used to simulate and configure session properties for controlled test scenarios:
279+
280+
```dart
281+
withServerpod('test group', (sessionBuilder, endpoints) {
282+
test('endpoint test', () async {
283+
var result = await endpoints.users.getUser(sessionBuilder, 123);
284+
expect(result.name, 'John');
285+
});
286+
287+
test('authenticated endpoint test', () async {
288+
const int userId = 1234;
289+
290+
var authenticatedSessionBuilder = sessionBuilder.copyWith(
291+
authentication: AuthenticationOverride.authenticationInfo(userId, {Scope('user')}),
292+
);
293+
294+
var result = await endpoints.users.updateProfile(
295+
authenticatedSessionBuilder,
296+
ProfileData(name: 'Jane')
297+
);
298+
expect(result.success, isTrue);
299+
});
300+
});
301+
```
302+
303+
For detailed testing strategies, see the [testing documentation](./19-testing/01-get-started.md).

0 commit comments

Comments
 (0)