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
docs: rust serverless functions hooks example (#464)
* docs: rust serverless functions hooks example
Signed-off-by: David Dal Busco <david.dalbusco@outlook.com>
* chore: real-world example
Signed-off-by: David Dal Busco <david.dalbusco@outlook.com>
* 📄 Update LLMs.txt snapshot for PR review
---------
Signed-off-by: David Dal Busco <david.dalbusco@outlook.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This creates a `juno.config` file — in TypeScript, JavaScript, or JSON depending on your preferences — at the root of your project. It contains metadata such as the Satellite ID used during SDK initialization.
189
197
198
+
---
199
+
200
+
## Connecting Code and Config
201
+
190
202
If you're using **Next.js** or **Vite**, we recommend installing the official plugin. It automatically loads values from your config file and injects them into your build as environment variables.
191
203
192
204
This means you can call `initSatellite()` without passing any parameters, the SDK will read them automatically from `process.env` or `import.meta.env`.
@@ -3192,7 +3204,7 @@ juno dev start
3192
3204
npm run dev
3193
3205
```
3194
3206
3195
-
6. **Build the serverless functions** (in a separate terminal):
3207
+
7. **Build the serverless functions** (in a separate terminal):
3196
3208
3197
3209
```
3198
3210
juno functions build
@@ -3274,6 +3286,182 @@ These crates are used to build and extend serverless functions in Rust with Juno
3274
3286
* [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions (e.g., `#[assert_set_doc]`, `#[on_delete_doc]`).
3275
3287
* [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling.
3276
3288
3289
+
# Rust Hooks Example
3290
+
3291
+
This example shows how to use **hooks in Rust** to customize how your Juno **Satellite** behaves. Hooks let you react to things like documents being created or deleted, or assets being uploaded — so you can run custom logic automatically on the backend.
3292
+
3293
+
The project includes a simple frontend to help you trigger and test the backend logic, but the real focus is on how the Rust serverless function works.
3294
+
3295
+
You can browse the source code here: [github.com/junobuild/examples/tree/main/rust/hooks](https://github.com/junobuild/examples/tree/main/rust/hooks)
* **Serverless Hooks in Rust**: Demonstrates how to react to data and asset operations using hooks in Rust serverless functions.
3310
+
* **Multiple Hook Types**: Includes hooks for document set, set-many, delete, and asset upload operations.
3311
+
* **Serverless Integration**: Runs as a Satellite function and integrates with Juno's datastore and authentication system.
3312
+
* **Minimal UI for Testing**: A simple frontend is included to test and demonstrate the hook logic in action.
3313
+
3314
+
---
3315
+
3316
+
## Main Backend Components
3317
+
3318
+
* **src/satellite/src/lib.rs**: The core Rust logic for the Satellite serverless function. Implements hooks for various operations (set, set-many, delete, upload).
3319
+
* **src/satellite/Cargo.toml**: Rust package configuration for the Satellite function.
3320
+
3321
+
---
3322
+
3323
+
## Example: Hooks in Rust
3324
+
3325
+
Here’s the actual Rust logic from `lib.rs`:
3326
+
3327
+
```
3328
+
use ic_cdk::print;use junobuild_macros::{on_delete_doc, on_set_doc, on_set_many_docs, on_upload_asset};use junobuild_satellite::{ include_satellite, set_doc_store, OnDeleteDocContext, OnSetDocContext, OnSetManyDocsContext, OnUploadAssetContext, SetDoc,};use junobuild_utils::{decode_doc_data, encode_doc_data};use junobuild_utils::{DocDataBigInt, DocDataPrincipal};use serde::{Deserialize, Serialize};#[derive(Serialize, Deserialize)]struct Person { yolo: bool, hello: String, principal: DocDataPrincipal, value: DocDataBigInt,}// Hook that runs when a document is set in the "demo" collection#[on_set_doc(collections = ["demo"])]async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> { // Decode the document into our Person struct let mut data: Person = decode_doc_data(&context.data.data.after.data)?; // Log some values for debugging print(format!("[on_set_doc] Caller: {}", context.caller.to_text())); print(format!("[on_set_doc] Collection: {}", context.data.collection)); print(format!("[on_set_doc] Data: {} {}", data.principal.value, data.value.value)); // Modify the document before storing it again data.hello = format!("{} checked", data.hello); data.yolo = false; // Encode and re-store the updated document let encode_data = encode_doc_data(&data)?; let doc: SetDoc = SetDoc { data: encode_data, description: context.data.data.after.description, version: context.data.data.after.version, }; set_doc_store( context.caller, context.data.collection, context.data.key, doc, )?; Ok(())}// Hook that runs when multiple documents are set in the "demo" collection#[on_set_many_docs(collections = ["demo"])]async fn on_set_many_docs(context: OnSetManyDocsContext) -> Result<(), String> { print(format!("Many docs called {}", context.data.len())); Ok(())}// Hook that runs when a document is deleted#[on_delete_doc]async fn on_delete_doc(_context: OnDeleteDocContext) -> Result<(), String> { print("Bye bye"); Ok(())}// Hook that runs when an asset is uploaded to Storage#[on_upload_asset]async fn on_upload_asset(context: OnUploadAssetContext) -> Result<(), String> { print(format!("Asset uploaded {}", context.data.key.full_path)); Ok(())}// A sample query function, accessible via `query` calls#[ic_cdk::query]fn say() { print("Hello");}include_satellite!();
3329
+
```
3330
+
3331
+
**Explanation:**
3332
+
3333
+
* Defines a `Person` struct with fields for demo purposes.
3334
+
* Uses the `#[on_set_doc]` macro to run logic whenever a document is set in the `demo` collection. Updates the document and saves it back.
3335
+
* Uses the `#[on_set_many_docs]` macro to react to batch document sets in the `demo` collection.
3336
+
* Uses the `#[on_delete_doc]` macro to react to document deletions.
3337
+
* Uses the `#[on_upload_asset]` macro to react to asset uploads.
3338
+
* Includes a simple query endpoint (`say`) for demonstration.
3339
+
* `include_satellite!();` brings in the necessary boilerplate for the Juno Satellite runtime.
Requires the Juno CLI to be available `npm i -g @junobuild/cli`
3362
+
3363
+
```
3364
+
juno dev start
3365
+
```
3366
+
3367
+
4. **Create a Satellite** for local dev:
3368
+
3369
+
* Visit [http://localhost:5866](http://localhost:5866) and follow the instructions.
3370
+
* Update `juno.config.ts` with your Satellite ID.
3371
+
3372
+
5. **Create required collections**:
3373
+
3374
+
* `demo` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore)
3375
+
* `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage)
3376
+
3377
+
6. **Start the frontend dev server** (in a separate terminal):
3378
+
3379
+
```
3380
+
npm run dev
3381
+
```
3382
+
3383
+
7. **Build the serverless functions** (in a separate terminal):
3384
+
3385
+
```
3386
+
juno functions build
3387
+
```
3388
+
3389
+
The emulator will automatically upgrade your Satellite and live reload the changes.
3390
+
3391
+
---
3392
+
3393
+
## Juno-Specific Configuration
3394
+
3395
+
* **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. See the [Configuration reference](/docs/reference/configuration.md) for details.
3396
+
* **vite.config.ts**: Registers the `juno` plugin to inject environment variables automatically. See the [Vite Plugin reference](/docs/reference/plugins.md#vite-plugin) for more information.
3397
+
3398
+
---
3399
+
3400
+
## Production Deployment
3401
+
3402
+
* Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet.
3403
+
* Update `juno.config.ts` with the production Satellite ID.
3404
+
* Build and deploy the frontend:
3405
+
3406
+
```
3407
+
npm run buildjuno deploy
3408
+
```
3409
+
3410
+
* Build and upgrade the serverless functions:
3411
+
3412
+
```
3413
+
juno functions buildjuno functions upgrade
3414
+
```
3415
+
3416
+
---
3417
+
3418
+
## Notes
3419
+
3420
+
* This example focuses on the Rust serverless function hooks; the frontend is intentionally minimal and only included for demonstration purposes.
3421
+
* Use this project as a starting point for writing custom hooks and backend logic in Rust with Juno.
3422
+
3423
+
---
3424
+
3425
+
## Real-World Example
3426
+
3427
+
Want to see how assertions and serverless logic are used in a live project?
3428
+
3429
+
Check out [proposals.network](https://proposals.network), an open-source app built with Juno:
These crates are used to build and extend serverless functions in Rust with Juno:
3460
+
3461
+
* [junobuild-satellite](https://docs.rs/junobuild-satellite): Core features and runtime for building a Satellite in Rust, including hooks, assertions, and datastore integration.
3462
+
* [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions (e.g., `#[on_set_doc]`, `#[on_delete_doc]`).
3463
+
* [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling.
3464
+
3277
3465
# Using Juno with AI
3278
3466
3279
3467
If you're using AI to build with Juno, you can use our `llms.txt` files to help AI tools better understand the platform.
@@ -8503,6 +8691,18 @@ Make sure these two requirements are correctly met before restarting the command
8503
8691
8504
8692
---
8505
8693
8694
+
### A Satellite ID is not configured. Juno cannot be initialized.
8695
+
8696
+
If you encounter the error:
8697
+
8698
+
> A Satellite ID is not configured. Juno cannot be initialized.
8699
+
8700
+
This means `initSatellite()` from the SDK is being called without a proper configuration. Most likely, the plugin responsible for loading your `juno.config` file values isn't set up correctly or is missing entirely.
8701
+
8702
+
To resolve this issue, make sure your SDK is correctly configured by following the steps in the documentation: [Setup SDK > Configuration](/docs/setup-the-sdk.md#configuration)
8703
+
8704
+
---
8705
+
8506
8706
### Invalid character: "<"
8507
8707
8508
8708
When you scaffold an app with a template, the `juno.config` file includes placeholder values for the satellite IDs:
Copy file name to clipboardExpand all lines: .llms-snapshots/llms.txt
+1Lines changed: 1 addition & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -59,6 +59,7 @@ Juno is your self-contained serverless platform for building full-stack web apps
59
59
## Examples - Functions - Rust
60
60
61
61
- [Rust Assertions Example](https://juno.build/docs/examples/functions/rust/assertions.md): An example demonstrating how to write custom assertions in Rust for Juno serverless functions.
62
+
- [Rust Hooks Example](https://juno.build/docs/examples/functions/rust/hooks.md): An example demonstrating how to use serverless function hooks in Rust for Juno serverless functions.
0 commit comments