Skip to content

Commit 8f7dacf

Browse files
docs: asset generation example (#467)
* docs: asset generation example Signed-off-by: David Dal Busco <david.dalbusco@outlook.com> * fix: build 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 1f749df commit 8f7dacf

5 files changed

Lines changed: 413 additions & 4 deletions

File tree

.llms-snapshots/llms-full.txt

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,7 +1300,7 @@ juno functions publish --mode staging --src ./path/to/build.wasm.gz
13001300
```
13011301

13021302
* 📤 Uploads to the Satellite’s CDN release.
1303-
* 🔐 Requires access key with **write** role.
1303+
* 🔐 Requires access key with **editor** role.
13041304

13051305
#### b) Upgrade from CDN
13061306

@@ -3286,6 +3286,169 @@ These crates are used to build and extend serverless functions in Rust with Juno
32863286
* [junobuild-satellite](https://docs.rs/junobuild-satellite): Core features and runtime for building a Satellite in Rust, including hooks, assertions, and datastore integration.
32873287
* [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions.
32883288
* [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling.
3289+
* [junobuild-shared](https://docs.rs/junobuild-shared): Shared types and helpers for Juno projects. Used by all containers including the Console.
3290+
* [junobuild-storage](https://docs.rs/junobuild-storage): Storage helpers for working with assets and HTTP headers in Juno.
3291+
3292+
# Generating Assets with Rust Serverless Functions
3293+
3294+
This example demonstrates how to use **Rust serverless functions** to dynamically generate and store assets in **Juno Storage** from a **Satellite**. In this example, the generated assets are JSON files.
3295+
3296+
Each time a note is added through the frontend, the Satellite saves the note as an individual JSON file and updates a list of all notes as another JSON file. This pattern is useful for exposing structured, queryable data as static assets — consumable by your frontend or external services.
3297+
3298+
You can browse the source code here: [github.com/junobuild/examples/tree/main/rust/json](https://github.com/junobuild/examples/tree/main/rust/json)
3299+
3300+
---
3301+
3302+
## Folder Structure
3303+
3304+
```
3305+
rust/json/├── src/│ ├── satellite/ # Rust Satellite serverless function│ │ ├── src/│ │ │ ├── lib.rs # Main Rust logic for Satellite│ │ │ └── generators.rs# Helper logic for JSON generation/storage│ │ ├── satellite.did # Candid interface definition│ │ └── Cargo.toml # Rust package config│ ├── declarations/ # TypeScript declarations for Satellite│ ├── lib/ # Svelte frontend components, stores, types│ ├── routes/ # SvelteKit route files│ ├── app.html # Svelte app entry│ └── app.css # Styles├── juno.config.ts # Juno Satellite configuration├── package.json # Frontend dependencies└── ...
3306+
```
3307+
3308+
---
3309+
3310+
## Key Features
3311+
3312+
* **Serverless JSON Generation**: Demonstrates how to generate and store JSON files in Storage from Rust serverless functions.
3313+
* **Automatic List Updates**: Each note addition updates both the individual note JSON and a list of all notes as JSON.
3314+
* **Integration with Juno Storage**: Uses Juno's Storage API to expose JSON assets on the web.
3315+
* **Minimal SvelteKit UI**: A simple SvelteKit frontend is included to test and demonstrate the logic.
3316+
3317+
---
3318+
3319+
## Main Backend Components
3320+
3321+
* **src/satellite/src/lib.rs**: The entry point for the Satellite serverless function. Triggers JSON generation and list update on document set.
3322+
* **src/satellite/src/generators.rs**: Helper logic for encoding notes and lists as JSON and storing them as assets.
3323+
* **src/satellite/Cargo.toml**: Rust package configuration for the Satellite function.
3324+
3325+
---
3326+
3327+
## Example: Generating and Storing JSON
3328+
3329+
Here’s the actual Rust logic from `lib.rs` and `generators.rs`:
3330+
3331+
```
3332+
// src/satellite/src/lib.rsmod generators;use crate::generators::{generate_list_of_notes, generate_note};use junobuild_macros::on_set_doc;use junobuild_satellite::{include_satellite, OnSetDocContext};/// Hook triggered whenever a document is set (e.g., added or updated)./// This example:/// - Stores the updated document as an individual JSON file in Storage/// - Updates a list of all note filenames as a separate JSON file#[on_set_doc]async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> { ic_cdk::print("Let's go!"); // Save the current note as a JSON asset generate_note(&context.data.key, &context.data.data.after)?; // Regenerate the list of notes as a JSON array generate_list_of_notes()?; Ok(())}// Boilerplate macro to include the all Satellite runtimeinclude_satellite!();
3333+
```
3334+
3335+
```
3336+
// src/satellite/src/generators.rsuse junobuild_satellite::{list_assets_store, set_asset_handler, Doc};use junobuild_shared::types::core::Key;use junobuild_shared::types::list::ListParams;use junobuild_storage::http::types::HeaderField;use junobuild_storage::types::store::AssetKey;use junobuild_utils::{decode_doc_data, encode_doc_data_to_string};use serde::{Deserialize, Serialize};/// Represents the expected shape of a note stored in the Datastore#[derive(Serialize, Deserialize)]struct Note { text: String, url: Option<String>,}/// Encodes a note document as JSON and stores it as a `.json` file in Storagepub fn generate_note(key: &Key, doc: &Doc) -> Result<(), String> { let note: Note = decode_doc_data(&doc.data)?; let json = encode_doc_data_to_string(&note)?; let name = format!("{}.json", key); insert_asset(&name, &json)}const STORAGE_COLLECTION: &str = "json";/// Lists all assets in the `json` collection and stores their filenames/// in a `notes.json` file — a JSON array of all note filenamespub fn generate_list_of_notes() -> Result<(), String> { let params: ListParams = ListParams { matcher: None, paginate: None, order: None, owner: None, }; let result = list_assets_store(ic_cdk::id(), STORAGE_COLLECTION, &params)?; // Extract the full paths of all assets in the collection let list_of_keys: Vec<String> = result .items .iter() .map(|(_, asset)| asset.key.full_path.clone()) .collect(); let json = encode_doc_data_to_string(&list_of_keys)?; let name = "notes.json".to_string(); insert_asset(&name, &json)?; Ok(())}/// Stores a given string as an asset in the `json` collectionfn insert_asset(name: &String, json: &String) -> Result<(), String> { ic_cdk::print(format!("Json: {} {}", name, json)); let full_path = format!("/{}/{}", STORAGE_COLLECTION, name); let key: AssetKey = AssetKey { name: name.clone(), full_path: full_path.clone(), token: None, collection: STORAGE_COLLECTION.to_string(), owner: ic_cdk::id(), description: None, }; // Set appropriate headers for serving JSON let headers = vec![HeaderField( "content-type".to_string(), "application/json".to_string(), )]; // Upload asset to Juno Storage set_asset_handler(&key, &json.as_bytes().to_vec(), &headers)?; ic_cdk::print(format!( "Asset saved in Storage: http://{}.localhost:5987{}", ic_cdk::id(), full_path )); Ok(())}
3337+
```
3338+
3339+
**Explanation:**
3340+
3341+
* When a note is added or updated, the `on_set_doc` hook is triggered.
3342+
* The note is encoded as JSON and stored as an asset in the `json` collection.
3343+
* A list of all note asset paths is also generated and stored as `notes.json`.
3344+
* These JSON assets are accessible via the Storage API and can be fetched by the frontend or other clients.
3345+
3346+
---
3347+
3348+
## How to Run
3349+
3350+
1. **Clone the repo**:
3351+
3352+
```
3353+
git clone https://github.com/junobuild/examplescd rust/hooks
3354+
```
3355+
3356+
2. **Install dependencies**:
3357+
3358+
```
3359+
npm install
3360+
```
3361+
3362+
3. **Start Juno local emulator**:
3363+
3364+
**Important:**
3365+
3366+
Requires the Juno CLI to be available `npm i -g @junobuild/cli`
3367+
3368+
```
3369+
juno dev start
3370+
```
3371+
3372+
4. **Create a Satellite** for local dev:
3373+
3374+
* Visit [http://localhost:5866](http://localhost:5866) and follow the instructions.
3375+
* Update `juno.config.ts` with your Satellite ID.
3376+
3377+
5. **Create required collections**:
3378+
3379+
* `demo` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore)
3380+
* `json` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage)
3381+
3382+
6. **Start the frontend dev server** (in a separate terminal):
3383+
3384+
```
3385+
npm run dev
3386+
```
3387+
3388+
7. **Build the serverless functions** (in a separate terminal):
3389+
3390+
```
3391+
juno functions build
3392+
```
3393+
3394+
The emulator will automatically upgrade your Satellite and live reload the changes.
3395+
3396+
---
3397+
3398+
## Juno-Specific Configuration
3399+
3400+
* **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. See the [Configuration reference](/docs/reference/configuration.md) for details.
3401+
* **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.
3402+
3403+
---
3404+
3405+
## Production Deployment
3406+
3407+
* Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet.
3408+
* Update `juno.config.ts` with the production Satellite ID.
3409+
* Build and deploy the frontend:
3410+
3411+
```
3412+
npm run buildjuno deploy
3413+
```
3414+
3415+
* Build and upgrade the serverless functions:
3416+
3417+
```
3418+
juno functions buildjuno functions upgrade
3419+
```
3420+
3421+
---
3422+
3423+
## Notes
3424+
3425+
* This example focuses on the Rust serverless function. The frontend is intentionally minimal and included only for demonstration.
3426+
* Use this project as a starting point for generate dynamic assets using Juno and Rust.
3427+
3428+
---
3429+
3430+
## References
3431+
3432+
* [Serverless Functions Guide](/docs/guides/rust.md)
3433+
* [Rust Functions Development](/docs/build/functions.md)
3434+
* [Rust SDK Reference](/docs/reference/functions/rust/sdk.md)
3435+
* [Rust Utils Reference](/docs/reference/functions/rust/utils.md)
3436+
* [Run Local Development](/docs/guides/local-development.md)
3437+
* [CLI Reference](/docs/reference/cli.md)
3438+
* [Configuration Reference](/docs/reference/configuration.md)
3439+
* [Datastore Collections](/docs/build/datastore/collections.md)
3440+
3441+
---
3442+
3443+
## Crate Docs
3444+
3445+
These crates are used to build and extend serverless functions in Rust with Juno:
3446+
3447+
* [junobuild-satellite](https://docs.rs/junobuild-satellite): Core features and runtime for building a Satellite in Rust, including hooks, assertions, and datastore integration.
3448+
* [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions.
3449+
* [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling.
3450+
* [junobuild-shared](https://docs.rs/junobuild-shared): Shared types and helpers for Juno projects. Used by all containers including the Console.
3451+
* [junobuild-storage](https://docs.rs/junobuild-storage): Storage helpers for working with assets and HTTP headers in Juno.
32893452

32903453
# Mutating Documents with Rust Hooks
32913454

@@ -3458,6 +3621,8 @@ These crates are used to build and extend serverless functions in Rust with Juno
34583621
* [junobuild-satellite](https://docs.rs/junobuild-satellite): Core features and runtime for building a Satellite in Rust, including hooks, assertions, and datastore integration.
34593622
* [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions.
34603623
* [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling.
3624+
* [junobuild-shared](https://docs.rs/junobuild-shared): Shared types and helpers for Juno projects. Used by all containers including the Console.
3625+
* [junobuild-storage](https://docs.rs/junobuild-storage): Storage helpers for working with assets and HTTP headers in Juno.
34613626

34623627
# Using Juno with AI
34633628

@@ -5381,9 +5546,9 @@ name: Publish Serverless Functionson: workflow_dispatch: push: branches: [m
53815546

53825547
This action will build and publish your serverless function bundle.
53835548

5384-
If your access key has a **write** role, the changes will be automatically deployed to your Satellite's CDN.
5549+
If your access key is an **editor**, the changes will be automatically deployed to your Satellite's CDN.
53855550

5386-
If your key only has a **submit** role, the release will be submitted as a pending change for manual approval. To avoid errors in submit-only workflows, you can explicitly use the `--no-apply` flag to skip auto-application.
5551+
If your key is only a **submitter**, the release will be proposed as a pending change for manual approval. To avoid errors in submit-only workflows, you can explicitly use the `--no-apply` flag to skip auto-application.
53875552

53885553
```
53895554
- name: Publish uses: junobuild/juno-action@full with: args: functions publish --no-apply env: JUNO_TOKEN: ${{ secrets.JUNO_TOKEN }}

.llms-snapshots/llms.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Juno is your self-contained serverless platform for building full-stack web apps
5959
## Examples - Functions - Rust
6060

6161
- [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+
- [Generating Assets with Rust Serverless Functions](https://juno.build/docs/examples/functions/rust/generating-assets-with-hooks.md): An example showing how to dynamically generate and store assets (like JSON) in Storage using Rust in Juno Satellites.
6263
- [Mutating Documents with Rust Hooks](https://juno.build/docs/examples/functions/rust/mutating-docs-with-hooks.md): An example demonstrating how to modify and re-save documents in Juno Satellites using Rust hooks.
6364

6465
## Guides

docs/examples/functions/rust/components/references.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ These crates are used to build and extend serverless functions in Rust with Juno
1818
- [junobuild-satellite](https://docs.rs/junobuild-satellite): Core features and runtime for building a Satellite in Rust, including hooks, assertions, and datastore integration.
1919
- [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions.
2020
- [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling.
21+
- [junobuild-shared](https://docs.rs/junobuild-shared): Shared types and helpers for Juno projects. Used by all containers including the Console.
22+
- [junobuild-storage](https://docs.rs/junobuild-storage): Storage helpers for working with assets and HTTP headers in Juno.

0 commit comments

Comments
 (0)