Skip to content

Commit ce5975e

Browse files
docs: serverless functions rust assertions example (#460)
* docs: serverless functions rust assertions 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>
1 parent 3b77f20 commit ce5975e

4 files changed

Lines changed: 395 additions & 1 deletion

File tree

.llms-snapshots/llms-full.txt

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3077,6 +3077,170 @@ The following functions from `@junobuild/core` are used in this example:
30773077
| `uploadFile` | Upload a file to storage | [`src/components/Modal.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Modal.vue) | [Upload file](/docs/build/storage/development.md#upload-file) |
30783078
| `deleteAsset` | Delete a file from storage | [`src/components/Delete.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Delete.vue) | [Delete asset](/docs/build/storage/development.md#delete-asset) |
30793079

3080+
# Rust Assertions Example
3081+
3082+
This example demonstrates how to write a **custom assertion** in **Rust** for a Juno **serverless function**. It shows how to intercept and validate data operations—such as rejecting specific content—before it's written to the datastore.
3083+
3084+
The project includes a minimal frontend to help trigger and test the logic, but the primary focus is the backend assertion.
3085+
3086+
You can browse the source code here: [github.com/junobuild/examples/tree/main/rust/assertions](https://github.com/junobuild/examples/tree/main/rust/assertions)
3087+
3088+
---
3089+
3090+
## Folder Structure
3091+
3092+
```
3093+
rust/assertions/├── src/│ ├── satellite/ # Rust Satellite serverless function│ │ ├── src/│ │ │ └── lib.rs # Main Rust logic for Satellite│ │ ├── satellite.did # Candid interface definition│ │ └── Cargo.toml # Rust package config├── src/components/ # Minimal frontend React components├── juno.config.ts # Juno Satellite configuration├── package.json # Frontend dependencies└── ...
3094+
```
3095+
3096+
---
3097+
3098+
## Key Features
3099+
3100+
* **Custom Assertions in Rust**: Demonstrates how to reject or validate data before it's saved, using Rust serverless functions.
3101+
* **Serverless Integration**: Runs as a Satellite function and integrates with Juno's datastore and authentication system.
3102+
* **Minimal UI for Testing**: A simple frontend is included to test and demonstrate the assertion logic in action.
3103+
3104+
---
3105+
3106+
## Main Backend Components
3107+
3108+
* **src/satellite/src/lib.rs**: The core Rust logic for the Satellite serverless function. Implements the custom assertions (e.g., only allow certain valid inputs, etc.).
3109+
* **src/satellite/Cargo.toml**: Rust package configuration for the Satellite function.
3110+
3111+
---
3112+
3113+
## Example: Custom Assertion in Rust
3114+
3115+
Here’s the actual Rust logic from `lib.rs`:
3116+
3117+
```
3118+
// This example defines a custom assertion in a Juno Satellite using Rust.// It checks if a document being saved to the "notes" collection contains the word "hello".// If it does, the assertion rejects the operation and logs a message.use ic_cdk::print;use junobuild_macros::assert_set_doc;use junobuild_satellite::{include_satellite, AssertSetDocContext};use junobuild_utils::decode_doc_data;use serde::{Deserialize, Serialize};#[derive(Serialize, Deserialize)]struct Note { text: String, url: Option<String>,}#[assert_set_doc(collections = ["notes"])]fn assert_set_doc(context: AssertSetDocContext) -> Result<(), String> { let note = decode_doc_data::<Note>(&context.data.data.proposed.data)?; if note.text.to_lowercase().contains("hello") { print(format!("❌ Rejected note containing 'hello': {}", note.text)); return Err("The note should not contain the keyword 'hello'.".to_string()); } print(format!("✅ Note accepted: {}", note.text)); Ok(())}include_satellite!();
3119+
```
3120+
3121+
**Explanation:**
3122+
3123+
* Defines a `Note` struct with `text` and optional `url` fields. Similar as the fields used in the frontend.
3124+
* Uses the `#[assert_set_doc]` macro to create a custom assertion for the `notes` collection.
3125+
* When a note is created or updated, the assertion checks if the note's text contains the word "hello" (case-insensitive).
3126+
* If it does, the note is rejected and an error message is returned; otherwise, the note is accepted.
3127+
* Prints a message to the log for both accepted and rejected notes.
3128+
* `include_satellite!();` brings in the necessary boilerplate for the Juno Satellite runtime.
3129+
3130+
---
3131+
3132+
## How to Run
3133+
3134+
1. **Clone the repo**:
3135+
3136+
```
3137+
git clone https://github.com/junobuild/examplescd rust/assertions
3138+
```
3139+
3140+
2. **Install dependencies**:
3141+
3142+
```
3143+
npm install
3144+
```
3145+
3146+
3. **Start Juno local emulator**:
3147+
3148+
**Important:**
3149+
3150+
Requires the Juno CLI to be available `npm i -g @junobuild/cli`
3151+
3152+
```
3153+
juno dev start
3154+
```
3155+
3156+
4. **Create a Satellite** for local dev:
3157+
3158+
* Visit [http://localhost:5866](http://localhost:5866) and follow the instructions.
3159+
* Update `juno.config.ts` with your Satellite ID.
3160+
3161+
5. **Create required collections**:
3162+
3163+
* `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore)
3164+
* `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage)
3165+
3166+
6. **Start the frontend dev server** (in a separate terminal):
3167+
3168+
```
3169+
npm run dev
3170+
```
3171+
3172+
6. **Build the serverless functions** (in a separate terminal):
3173+
3174+
```
3175+
juno functions build
3176+
```
3177+
3178+
The emulator will automatically upgrade your Satellite and live reload the changes.
3179+
3180+
---
3181+
3182+
## Juno-Specific Configuration
3183+
3184+
* **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. See the [Configuration reference](/docs/reference/configuration.md) for details.
3185+
* **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.
3186+
3187+
---
3188+
3189+
## Production Deployment
3190+
3191+
* Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet.
3192+
* Update `juno.config.ts` with the production Satellite ID.
3193+
* Build and deploy the frontend:
3194+
3195+
```
3196+
npm run buildjuno deploy
3197+
```
3198+
3199+
* Build and upgrade the serverless functions:
3200+
3201+
```
3202+
juno functions buildjuno functions upgrade
3203+
```
3204+
3205+
---
3206+
3207+
## Notes
3208+
3209+
* This example focuses on the Rust serverless function; the frontend is intentionally minimal and only included for demonstration purposes.
3210+
* Use this project as a starting point for writing custom assertions and backend logic in Rust with Juno.
3211+
3212+
---
3213+
3214+
## Real-World Example
3215+
3216+
Want to see how assertions and serverless logic are used in a live project?
3217+
3218+
Check out [proposals.network](https://proposals.network), an open-source app built with Juno:
3219+
3220+
* GitHub: [github.com/peterpeterparker/proposals.network](https://github.com/peterpeterparker/proposals.network)
3221+
* Example logic: [src/satellite/src/lib.rs](https://github.com/peterpeterparker/proposals.network/blob/main/src/satellite/src/lib.rs)
3222+
3223+
This app uses:
3224+
3225+
* `#[on_delete_doc]` and `#[assert_delete_doc]` to validate and clean up related documents and assets
3226+
* Shared helper modules like `assert`, `delete`, and `types` to keep logic organized
3227+
* A real-world pattern of chaining asset/document deletions with assertions
3228+
3229+
It’s a great reference for more advanced setups and multi-collection coordination.
3230+
3231+
---
3232+
3233+
## References
3234+
3235+
* [Serverless Functions Guide](/docs/guides/rust.md)
3236+
* [Rust Functions Development](/docs/build/functions.md)
3237+
* [Rust SDK Reference](/docs/reference/functions/rust/sdk.md)
3238+
* [Rust Utils Reference](/docs/reference/functions/rust/utils.md)
3239+
* [Run Local Development](/docs/guides/local-development.md)
3240+
* [CLI Reference](/docs/reference/cli.md)
3241+
* [Configuration Reference](/docs/reference/configuration.md)
3242+
* [Datastore Collections](/docs/build/datastore/collections.md)
3243+
30803244
# Using Juno with AI
30813245

30823246
If you're using AI to build with Juno, you can use our `llms.txt` files to help AI tools better understand the platform.

.llms-snapshots/llms.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ Juno is your self-contained serverless platform for building full-stack web apps
5656
- [Vanilla JavaScript Example](https://juno.build/docs/examples/frontend/vanilla-javascript.md): A fullstack note-taking app built with vanilla JavaScript, and Tailwind CSS using Juno for authentication, data, and file storage.
5757
- [Vue Example](https://juno.build/docs/examples/frontend/vue.md): A fullstack note-taking app built with Vue, and Tailwind CSS using Juno for authentication, data, and file storage.
5858

59+
## Examples - Functions - Rust
60+
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+
5963
## Guides
6064

6165
- [AI](https://juno.build/docs/guides/ai.md): Learn how to use Juno's llms.txt files to provide AI tools with better context for building serverless functions, deploying satellites, and integrating the SDK.
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
---
2+
title: Rust Assertions Example
3+
description: An example demonstrating how to write custom assertions in Rust for Juno serverless functions.
4+
keywords: [rust, assertion, serverless, juno, satellite, example]
5+
sidebar_label: Assertions
6+
---
7+
8+
# Rust Assertions Example
9+
10+
This example demonstrates how to write a **custom assertion** in **Rust** for a Juno **serverless function**. It shows how to intercept and validate data operations—such as rejecting specific content—before it's written to the datastore.
11+
12+
The project includes a minimal frontend to help trigger and test the logic, but the primary focus is the backend assertion.
13+
14+
You can browse the source code here: [github.com/junobuild/examples/tree/main/rust/assertions](https://github.com/junobuild/examples/tree/main/rust/assertions)
15+
16+
---
17+
18+
## Folder Structure
19+
20+
```
21+
rust/assertions/
22+
├── src/
23+
│ ├── satellite/ # Rust Satellite serverless function
24+
│ │ ├── src/
25+
│ │ │ └── lib.rs # Main Rust logic for Satellite
26+
│ │ ├── satellite.did # Candid interface definition
27+
│ │ └── Cargo.toml # Rust package config
28+
├── src/components/ # Minimal frontend React components
29+
├── juno.config.ts # Juno Satellite configuration
30+
├── package.json # Frontend dependencies
31+
└── ...
32+
```
33+
34+
---
35+
36+
## Key Features
37+
38+
- **Custom Assertions in Rust**: Demonstrates how to reject or validate data before it's saved, using Rust serverless functions.
39+
- **Serverless Integration**: Runs as a Satellite function and integrates with Juno's datastore and authentication system.
40+
- **Minimal UI for Testing**: A simple frontend is included to test and demonstrate the assertion logic in action.
41+
42+
---
43+
44+
## Main Backend Components
45+
46+
- **src/satellite/src/lib.rs**: The core Rust logic for the Satellite serverless function. Implements the custom assertions (e.g., only allow certain valid inputs, etc.).
47+
- **src/satellite/Cargo.toml**: Rust package configuration for the Satellite function.
48+
49+
---
50+
51+
## Example: Custom Assertion in Rust
52+
53+
Here’s the actual Rust logic from `lib.rs`:
54+
55+
```rust
56+
// This example defines a custom assertion in a Juno Satellite using Rust.
57+
// It checks if a document being saved to the "notes" collection contains the word "hello".
58+
// If it does, the assertion rejects the operation and logs a message.
59+
60+
use ic_cdk::print;
61+
use junobuild_macros::assert_set_doc;
62+
use junobuild_satellite::{include_satellite, AssertSetDocContext};
63+
use junobuild_utils::decode_doc_data;
64+
use serde::{Deserialize, Serialize};
65+
66+
#[derive(Serialize, Deserialize)]
67+
struct Note {
68+
text: String,
69+
url: Option<String>,
70+
}
71+
72+
#[assert_set_doc(collections = ["notes"])]
73+
fn assert_set_doc(context: AssertSetDocContext) -> Result<(), String> {
74+
let note = decode_doc_data::<Note>(&context.data.data.proposed.data)?;
75+
76+
if note.text.to_lowercase().contains("hello") {
77+
print(format!("❌ Rejected note containing 'hello': {}", note.text));
78+
return Err("The note should not contain the keyword 'hello'.".to_string());
79+
}
80+
81+
print(format!("✅ Note accepted: {}", note.text));
82+
83+
Ok(())
84+
}
85+
86+
include_satellite!();
87+
```
88+
89+
**Explanation:**
90+
91+
- Defines a `Note` struct with `text` and optional `url` fields. Similar as the fields used in the frontend.
92+
- Uses the `#[assert_set_doc]` macro to create a custom assertion for the `notes` collection.
93+
- When a note is created or updated, the assertion checks if the note's text contains the word "hello" (case-insensitive).
94+
- If it does, the note is rejected and an error message is returned; otherwise, the note is accepted.
95+
- Prints a message to the log for both accepted and rejected notes.
96+
- `include_satellite!();` brings in the necessary boilerplate for the Juno Satellite runtime.
97+
98+
---
99+
100+
## How to Run
101+
102+
1. **Clone the repo**:
103+
104+
```bash
105+
git clone https://github.com/junobuild/examples
106+
cd rust/assertions
107+
```
108+
109+
2. **Install dependencies**:
110+
111+
```bash
112+
npm install
113+
```
114+
115+
3. **Start Juno local emulator**:
116+
117+
:::important
118+
119+
Requires the Juno CLI to be available `npm i -g @junobuild/cli`
120+
121+
:::
122+
123+
```bash
124+
juno dev start
125+
```
126+
127+
4. **Create a Satellite** for local dev:
128+
129+
- Visit [http://localhost:5866](http://localhost:5866) and follow the instructions.
130+
- Update `juno.config.ts` with your Satellite ID.
131+
132+
5. **Create required collections**:
133+
134+
- `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore)
135+
- `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage)
136+
137+
6. **Start the frontend dev server** (in a separate terminal):
138+
139+
```bash
140+
npm run dev
141+
```
142+
143+
6. **Build the serverless functions** (in a separate terminal):
144+
145+
```bash
146+
juno functions build
147+
```
148+
149+
The emulator will automatically upgrade your Satellite and live reload the changes.
150+
151+
---
152+
153+
## Juno-Specific Configuration
154+
155+
- **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. See the [Configuration reference](../../../reference/configuration.mdx) for details.
156+
- **vite.config.ts**: Registers the `juno` plugin to inject environment variables automatically. See the [Vite Plugin reference](../../../reference/plugins.mdx#vite-plugin) for more information.
157+
158+
---
159+
160+
## Production Deployment
161+
162+
- Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet.
163+
- Update `juno.config.ts` with the production Satellite ID.
164+
- Build and deploy the frontend:
165+
166+
```bash
167+
npm run build
168+
juno deploy
169+
```
170+
171+
- Build and upgrade the serverless functions:
172+
173+
```bash
174+
juno functions build
175+
juno functions upgrade
176+
```
177+
178+
---
179+
180+
## Notes
181+
182+
- This example focuses on the Rust serverless function; the frontend is intentionally minimal and only included for demonstration purposes.
183+
- Use this project as a starting point for writing custom assertions and backend logic in Rust with Juno.
184+
185+
---
186+
187+
## Real-World Example
188+
189+
Want to see how assertions and serverless logic are used in a live project?
190+
191+
Check out [proposals.network](https://proposals.network), an open-source app built with Juno:
192+
193+
- GitHub: [github.com/peterpeterparker/proposals.network](https://github.com/peterpeterparker/proposals.network)
194+
- Example logic: [src/satellite/src/lib.rs](https://github.com/peterpeterparker/proposals.network/blob/main/src/satellite/src/lib.rs)
195+
196+
This app uses:
197+
198+
- `#[on_delete_doc]` and `#[assert_delete_doc]` to validate and clean up related documents and assets
199+
- Shared helper modules like `assert`, `delete`, and `types` to keep logic organized
200+
- A real-world pattern of chaining asset/document deletions with assertions
201+
202+
It’s a great reference for more advanced setups and multi-collection coordination.
203+
204+
---
205+
206+
## References
207+
208+
- [Serverless Functions Guide](../../../guides/rust.mdx)
209+
- [Rust Functions Development](../../../build/functions/index.md)
210+
- [Rust SDK Reference](../../../reference/functions/rust/sdk.mdx)
211+
- [Rust Utils Reference](../../../reference/functions/rust/utils.mdx)
212+
- [Run Local Development](../../../guides/local-development.mdx)
213+
- [CLI Reference](../../../reference/cli)
214+
- [Configuration Reference](../../../reference/configuration.mdx)
215+
- [Datastore Collections](../../../build/datastore/collections.md)

0 commit comments

Comments
 (0)