You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/06-concepts/01-working-with-endpoints.md
+170-7Lines changed: 170 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -44,7 +44,6 @@ apiServer:
44
44
publicHost: localhost # Change this line
45
45
publicPort: 8080
46
46
publicScheme: http
47
-
...
48
47
```
49
48
50
49
:::info
@@ -77,7 +76,7 @@ The return type must be a typed Future. Supported return types are the same as f
77
76
78
77
### Ignore an entire `Endpoint` class
79
78
80
-
If you want the code generator to ignore an endpoint definition, you can annotate either the entire class or individual methods with `@doNotGenerate`. This can be useful if you want to keep the definition in your codebase without generating server or client bindings for it.
79
+
If you want the code generator to ignore an endpoint definition, you can annotate either the entire class or individual methods with `@doNotGenerate`. This can be useful if you want to keep the definition in your codebase without generating server or client bindings for it.
81
80
82
81
```dart
83
82
import 'package:serverpod/serverpod.dart';
@@ -115,7 +114,7 @@ In this case the `ExampleEndpoint` will only expose the `hello` method, whereas
115
114
116
115
## Endpoint method inheritance
117
116
118
-
Endpoints can be based on other endpoints using inheritance, like `class ChildEndpoint extends ParentEndpoint`. If the parent endpoint was marked as `abstract` or `@doNotGenerate`, no client code is generated for it, but a client will be generated for your subclass – as long as it does not opt out again.
117
+
Endpoints can be based on other endpoints using inheritance, like `class ChildEndpoint extends ParentEndpoint`. If the parent endpoint was marked as `abstract` or `@doNotGenerate`, no client code is generated for it, but a client will be generated for your subclass – as long as it does not opt out again.
119
118
Inheritance gives you the possibility to modify the behavior of `Endpoint` classes defined in other Serverpod modules.
120
119
121
120
Currently, there are the following possibilities to extend another `Endpoint` class:
@@ -159,8 +158,7 @@ abstract class CalculatorEndpoint extends Endpoint {
159
158
class MyCalculatorEndpoint extends CalculatorEndpoint {}
160
159
```
161
160
162
-
The generated client code will only be able to access `MyCalculatorEndpoint`, as the abstract `CalculatorEndpoint` is not exposed on the server.
163
-
`MyCalculatorEndpoint`exposes the `add` method it inherited from `CalculatorEndpoint`.
161
+
Since `CalculatorEndpoint` is `abstract`, it will not be exposed on the server. However, an abstract client class will be generated, which will be extended by the class generated from `MyCalculatorEndpoint`. The concrete client exposes the `add` method it inherited from `CalculatorEndpoint`. See [Client-side endpoint inheritance](#client-side-endpoint-inheritance) for more details on how abstract endpoints are represented on the client.
164
162
165
163
#### Extending an `abstract` `Endpoint` class
166
164
@@ -195,7 +193,7 @@ class CalculatorEndpoint extends Endpoint {
195
193
class MyCalculatorEndpoint extends CalculatorEndpoint {}
196
194
```
197
195
198
-
Since `CalculatorEndpoint` is marked as `@doNotGenerate` it will not be exposed on the server. Only `MyCalculatorEndpoint` will be accessible from the client, which provides the inherited `add` methods from its parent class.
196
+
Since `CalculatorEndpoint` is marked as `@doNotGenerate`, it will not be exposed on the server and no client class will be generated for it. Only `MyCalculatorEndpoint` will be accessible from the client, which provides the inherited `add` methods from its parent class. Unlike abstract endpoints, when a parent is marked with `@doNotGenerate`, the generated client class will implement the base endpoint class directly rather than extending a generated abstract parent class.
199
197
200
198
### Overriding endpoint methods
201
199
@@ -248,7 +246,7 @@ class AdderEndpoint extends CalculatorEndpoint {
248
246
```
249
247
250
248
Since `CalculatorEndpoint` is `abstract`, it will not be exposed on the server. `AdderEndpoint` inherits all methods from its parent class, but since it opts to hide `subtract` by annotating it with `@doNotGenerate` only the `add` method will be exposed.
251
-
Don't worry about the exception in the `subtract` implementation. That is only added to satisfy the Dart compiler –in practice, nothing will ever call this method on `AdderEndpoint`.
249
+
Don't worry about the exception in the `subtract` implementation. That is only added to satisfy the Dart compiler –in practice, nothing will ever call this method on `AdderEndpoint`.
252
250
253
251
Hiding endpoints from a super class is only appropriate in case the parent `class` is `abstract` or annotated with `@doNotGenerate`. Otherwise, the method that should be hidden on the child would still be accessible via the parent class.
254
252
@@ -306,3 +304,168 @@ abstract class AdminEndpoint extends Endpoint {
306
304
```
307
305
308
306
Again, just have your custom endpoint extend `AdminEndpoint` and you can be sure that the user has the appropriate permissions.
307
+
308
+
## Client-side endpoint inheritance
309
+
310
+
When you use endpoint inheritance on the server, Serverpod generates matching client-side classes that mirror your inheritance hierarchy. This allows you to write type-safe client code that works with abstract endpoint types.
311
+
312
+
### Abstract endpoint client generation
313
+
314
+
When you define an abstract endpoint on the server, Serverpod generates an abstract client endpoint class. This is particularly useful for module developers who want to provide base functionality that users can extend.
315
+
316
+
**Server-side abstract endpoint:**
317
+
318
+
```dart
319
+
import 'package:serverpod/serverpod.dart';
320
+
321
+
abstract class CalculatorEndpoint extends Endpoint {
322
+
Future<int> add(Session session, int a, int b) async {
323
+
return a + b;
324
+
}
325
+
}
326
+
```
327
+
328
+
**Generated client-side abstract class:**
329
+
330
+
```dart
331
+
abstract class EndpointCalculator extends EndpointRef {
Future<int> add(int a, int b) => caller.callServerEndpoint<int>(
359
+
'myCalculator',
360
+
'add',
361
+
{'a': a, 'b': b},
362
+
);
363
+
364
+
Future<int> subtract(int a, int b) => caller.callServerEndpoint<int>(
365
+
'myCalculator',
366
+
'subtract',
367
+
{'a': a, 'b': b},
368
+
);
369
+
}
370
+
```
371
+
372
+
### Using `getEndpointOfType` for type-safe endpoint access
373
+
374
+
When working with abstract endpoints, you can use the `getEndpointOfType` method to retrieve concrete endpoint instances by their type. This is especially useful when writing code that depends on abstract endpoint interfaces provided by modules.
375
+
376
+
```dart
377
+
// Get an endpoint by its type
378
+
var calculator = client.getEndpointOfType<EndpointCalculator>();
379
+
380
+
// Now you can call methods defined in the abstract base class
381
+
var result = await calculator.add(5, 3);
382
+
```
383
+
384
+
The `getEndpointOfType` method will:
385
+
386
+
- Return the single endpoint of the requested type if only one exists.
387
+
- Throw `ServerpodClientEndpointNotFound` if no endpoint of that type is found.
388
+
- Throw `ServerpodClientMultipleEndpointsFound` if multiple endpoints of that type exist.
389
+
390
+
#### Disambiguating multiple endpoints
391
+
392
+
If you have multiple concrete implementations of the same abstract endpoint, you can disambiguate by providing the endpoint name:
393
+
394
+
```dart
395
+
// Server-side: Two implementations of the same abstract endpoint
396
+
class BasicCalculatorEndpoint extends CalculatorEndpoint {}
397
+
class AdvancedCalculatorEndpoint extends CalculatorEndpoint {
398
+
Future<int> multiply(Session session, int a, int b) async {
399
+
return a * b;
400
+
}
401
+
}
402
+
```
403
+
404
+
```dart
405
+
// Client-side: Specify which implementation you want
406
+
var basicCalc = client.getEndpointOfType<EndpointCalculator>('basicCalculator');
407
+
var advancedCalc = client.getEndpointOfType<EndpointCalculator>('advancedCalculator');
408
+
```
409
+
410
+
#### Use case: Module-provided abstract endpoints
411
+
412
+
This pattern is particularly powerful for modules. A module can provide an abstract endpoint that defines an interface, and users of the module can extend it to expose the functionality on their server:
413
+
414
+
**In a module (e.g., `serverpod_auth`):**
415
+
416
+
Declare an abstract endpoint with common methods on the server:
417
+
418
+
```dart
419
+
abstract class AuthSessionEndpoint extends Endpoint {
Write client-side code that depends on the generated abstract type and uses its methods:
431
+
432
+
```dart
433
+
class UserLoggedInWidget extends StatelessWidget {
434
+
final Client client;
435
+
436
+
UserLoggedInWidget({required this.client});
437
+
438
+
// Will throw if no concrete implementation of the endpoint exists.
439
+
EndpointAuthSession get sessionEndpoint =>
440
+
client.getEndpointOfType<EndpointAuthSession>();
441
+
442
+
@override
443
+
Widget build(BuildContext context) {
444
+
return FutureBuilder<bool>(
445
+
future: sessionEndpoint.isAuthenticated(),
446
+
builder: (context, snapshot) {
447
+
if (snapshot.connectionState == ConnectionState.waiting) {
448
+
return CircularProgressIndicator();
449
+
} else if (snapshot.hasError) {
450
+
return Text('Error: ${snapshot.error}');
451
+
} else if (snapshot.hasData && snapshot.data == true) {
452
+
return Text('User is logged in');
453
+
} else {
454
+
return Text('User is not logged in');
455
+
}
456
+
},
457
+
);
458
+
}
459
+
}
460
+
```
461
+
462
+
**In the user application:**
463
+
464
+
The user will just have to extend the abstract endpoint to expose it on their server. Then, any client code that depends on the abstract endpoint will work seamlessly, regardless of the concrete class name or location.
465
+
466
+
```dart
467
+
// Extend the module's abstract endpoint to expose it
468
+
class SessionEndpoint extends AuthSessionEndpoint {}
469
+
```
470
+
471
+
This approach allows module developers to provide reusable endpoint logic while giving application developers full control over which endpoints are exposed on their server.
0 commit comments