From 8cc866c7117bdf051dc259b5e6708af9ceaf4efc Mon Sep 17 00:00:00 2001 From: Crazelu Date: Thu, 8 Jan 2026 23:20:32 +0100 Subject: [PATCH] docs: Type-safe code generated interface for future calls --- .../01-overview.md} | 80 +----------------- docs/06-concepts/14-scheduling/02-setup.md | 82 +++++++++++++++++++ docs/06-concepts/14-scheduling/03-legacy.md | 75 +++++++++++++++++ .../06-concepts/14-scheduling/_category_.json | 4 + docs/06-concepts/17-backward-compatibility.md | 2 + 5 files changed, 165 insertions(+), 78 deletions(-) rename docs/06-concepts/{14-scheduling.md => 14-scheduling/01-overview.md} (52%) create mode 100644 docs/06-concepts/14-scheduling/02-setup.md create mode 100644 docs/06-concepts/14-scheduling/03-legacy.md create mode 100644 docs/06-concepts/14-scheduling/_category_.json diff --git a/docs/06-concepts/14-scheduling.md b/docs/06-concepts/14-scheduling/01-overview.md similarity index 52% rename from docs/06-concepts/14-scheduling.md rename to docs/06-concepts/14-scheduling/01-overview.md index 1206f994..f4cd57d8 100644 --- a/docs/06-concepts/14-scheduling.md +++ b/docs/06-concepts/14-scheduling/01-overview.md @@ -1,92 +1,16 @@ -# Scheduling +# Overview With Serverpod you can schedule future work with the `future call` feature. Future calls are calls that will be invoked at a later time. An example is if you want to send a drip-email campaign after a user signs up. You can schedule a future call for a day, a week, or a month. The calls are stored in the database, so they will persist even if the server is restarted. A future call is guaranteed to only execute once across all your instances that are running, but execution failures are not handled automatically. It is your responsibility to schedule a new future call if the work was not able to complete. -## Future calls - -Creating a future call is simple, extend the `FutureCall` class and override the `invoke` method. The method takes two params the first being the [`Session`](sessions) object and the second being an optional SerializableModel ([See models](models)). - :::info The future call feature is not enabled when running Serverpod in serverless mode. ::: -```dart -import 'package:serverpod/serverpod.dart'; - -class ExampleFutureCall extends FutureCall { - @override - Future invoke(Session session, MyModelEntity? object) async { - // Do something interesting in the future here. - } -} -``` - -To let your Server get access to the future call you have to register it in the main run method in your `server.dart` file. You register the future call by calling `registerFutureCall` on the Serverpod object and giving it an instance of the future call together with a string that gives the future call a name. The name has to be globally unique and is used to later invoke the future call. - -```dart -void run(List args) async { - final pod = Serverpod( - args, - Protocol(), - Endpoints(), - ); - - ... - - pod.registerFutureCall(ExampleFutureCall(), 'exampleFutureCall'); - - ... -} -``` - -You are now able to register a future call to be invoked in the future by calling either `futureCallWithDelay` or `futureCallAtTime` depending on your needs. - -Invoke the future call 1 hour from now by calling `futureCallWithDelay`. - -```dart -await session.serverpod.futureCallWithDelay( - 'exampleFutureCall', - data, - const Duration(hours: 1), -); -``` - -Invoke the future call at a specific time and/or date in the future by calling `futureCallAtTime`. - -```dart -await session.serverpod.futureCallAtTime( - 'exampleFutureCall', - data, - DateTime(2025, 1, 1), -); -``` - -:::note -`data` is an object created from a class defined in one of your yaml files and has to be the same as the one you expect to receive in the future call. in the `model` folder, `data` may also be null if you don't need it. -::: - -When registering a future call it is also possible to give it an `identifier` so that it can be referenced later. The same identifier can be applied to multiple future calls. - -```dart -await session.serverpod.futureCallWithDelay( - 'exampleFutureCall', - data, - const Duration(hours: 1), - identifier: 'an-identifying-string', -); -``` - -This identifier can then be used to cancel all future calls registered with said identifier. - -```dart -await session.serverpod.cancelFutureCall('an-identifying-string'); -``` - ## Configuration -Future calls can be configured using options defined in the configuration files or environment variables. For a detailed list of configuration options, refer to the [Configuration](07-configuration.md) page. +Future calls can be configured using options defined in the configuration files or environment variables. For a detailed list of configuration options, refer to the [Configuration](../configuration) page. Below is an example of how you can configure future calls in a YAML file: diff --git a/docs/06-concepts/14-scheduling/02-setup.md b/docs/06-concepts/14-scheduling/02-setup.md new file mode 100644 index 00000000..b64ab729 --- /dev/null +++ b/docs/06-concepts/14-scheduling/02-setup.md @@ -0,0 +1,82 @@ +# Setup + +To create future calls, extend the `FutureCall` class and define the methods you wish to invoke at a later time. + +```dart +import 'package:serverpod/serverpod.dart'; + +class ExampleFutureCall extends FutureCall { + Future doWork(Session session, String data) async { + // Do something interesting in the future here. + } +} +``` + +:::info +For a method to be recognized by Serverpod as a future call, it must return a `Future` and take at least two parameters. The first parameter must be a [`Session`](../sessions) object. You can pass any serializable types as other parameters, and even use `List`, `Map`, `Set` or Dart records as long as they are typed. +::: + +Next, you need to generate the code for your future calls. You do this by running `serverpod generate` in the server directory of your project: + +```bash +$ cd your_server +$ serverpod generate +``` + +`serverpod generate` will create a type-safe interface for invoking the future calls in the server's `generated/future_calls.dart` file. This interface can be accessed from the Serverpod object. + +The future calls you create are registered by `Serverpod` after the server starts. + +```dart +void run(List args) async { + final pod = Serverpod( + args, + Protocol(), + Endpoints(), + ); + + ... + + await pod.start(); + + ... +} +``` + +You are now able to register a future call to be invoked in the future by calling either `callWithDelay` or `callAtTime` depending on your needs. + +Invoke the future call 1 hour from now by calling `callWithDelay`. + +```dart +await pod.futureCalls + .callWithDelay(const Duration(hours: 1)) + .example + .doWork('1'); +``` + +Invoke the future call at a specific time and/or date in the future by calling `callAtTime`. + +```dart +await pod.futureCalls + .callAtTime(DateTime(2026, 1, 1)) + .example + .doWork('2'); +``` + +When registering a future call, it is also possible to give it an `identifier` so that it can be referenced later. The same identifier can be applied to multiple future calls. + +```dart +await pod.futureCalls + .callWithDelay( + const Duration(hours: 1), + identifier: 'an-identifying-string', + ) + .example + .doWork('1'); +``` + +This identifier can then be used to cancel all future calls registered with said identifier. + +```dart +await pod.futureCalls.cancel('an-identifying-string'); +``` diff --git a/docs/06-concepts/14-scheduling/03-legacy.md b/docs/06-concepts/14-scheduling/03-legacy.md new file mode 100644 index 00000000..bf90cb3b --- /dev/null +++ b/docs/06-concepts/14-scheduling/03-legacy.md @@ -0,0 +1,75 @@ +# Legacy + +Creating a future call is simple, extend the `FutureCall` class and override the `invoke` method. The method takes two params the first being the [`Session`](../sessions) object and the second being an optional SerializableModel ([See models](../models)). + +```dart +import 'package:serverpod/serverpod.dart'; + +class ExampleFutureCall extends FutureCall { + @override + Future invoke(Session session, MyModelEntity? object) async { + // Do something interesting in the future here. + } +} +``` + +To let your Server get access to the future call you have to register it in the main run method in your `server.dart` file. You register the future call by calling `registerFutureCall` on the Serverpod object and giving it an instance of the future call together with a string that gives the future call a name. The name has to be globally unique and is used to later invoke the future call. + +```dart +void run(List args) async { + final pod = Serverpod( + args, + Protocol(), + Endpoints(), + ); + + ... + + pod.registerFutureCall(ExampleFutureCall(), 'exampleFutureCall'); + + ... +} +``` + +You are now able to register a future call to be invoked in the future by calling either `futureCallWithDelay` or `futureCallAtTime` depending on your needs. + +Invoke the future call 1 hour from now by calling `futureCallWithDelay`. + +```dart +await session.serverpod.futureCallWithDelay( + 'exampleFutureCall', + data, + const Duration(hours: 1), +); +``` + +Invoke the future call at a specific time and/or date in the future by calling `futureCallAtTime`. + +```dart +await session.serverpod.futureCallAtTime( + 'exampleFutureCall', + data, + DateTime(2025, 1, 1), +); +``` + +:::note +`data` is an object created from a class defined in one of your yaml files and has to be the same as the one you expect to receive in the future call. in the `model` folder, `data` may also be null if you don't need it. +::: + +When registering a future call, it is also possible to give it an `identifier` so that it can be referenced later. The same identifier can be applied to multiple future calls. + +```dart +await session.serverpod.futureCallWithDelay( + 'exampleFutureCall', + data, + const Duration(hours: 1), + identifier: 'an-identifying-string', +); +``` + +This identifier can then be used to cancel all future calls registered with said identifier. + +```dart +await session.serverpod.cancelFutureCall('an-identifying-string'); +``` diff --git a/docs/06-concepts/14-scheduling/_category_.json b/docs/06-concepts/14-scheduling/_category_.json new file mode 100644 index 00000000..dc0af035 --- /dev/null +++ b/docs/06-concepts/14-scheduling/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Scheduling", + "collapsed": true +} \ No newline at end of file diff --git a/docs/06-concepts/17-backward-compatibility.md b/docs/06-concepts/17-backward-compatibility.md index 8e1f3597..21729d67 100644 --- a/docs/06-concepts/17-backward-compatibility.md +++ b/docs/06-concepts/17-backward-compatibility.md @@ -7,6 +7,8 @@ Following a simple set of rules, your server will stay compatible with older app 1. __Avoid changing parameter names in endpoint methods.__ In the REST API Serverpod generates, the parameters are passed by name. This means that changing the parameter names of the endpoint methods will break backward compatibility. 2. __Do not delete endpoint methods or change their signature.__ Instead, add new methods if you must pass another set of parameters. Technically, you can add new named parameters if they are not required, but creating a new method may still feel cleaner. 3. __Avoid changing or removing fields and types in the serialized classes.__ However, you are free to add new fields as long as they are nullable. +4. __Avoid changing parameter names in future call methods.__ Changing the parameter names of the future call methods will break backward compatibility since parameters are passed by name. +5. __Do not delete future call methods or change their signature.__ Instead, add new methods if you must pass another set of parameters. ## Managing breaking changes with endpoint inheritance