diff --git a/.llms-snapshots/llms-full.txt b/.llms-snapshots/llms-full.txt index 872f26c6..14841048 100644 --- a/.llms-snapshots/llms-full.txt +++ b/.llms-snapshots/llms-full.txt @@ -145,6 +145,14 @@ If you intend to use Juno solely for **[hosting](/docs/build/hosting.md)** purpo --- +## TL;DR + +1. Call `initSatellite()` in your app code +2. Create a `juno.config` file at the root to define your Satellite +3. Connect code and config — preferably using the `@junobuild/nextjs-plugin` or `@junobuild/vite-plugin` + +--- + ## Initialization 1. Install the Juno SDK: @@ -187,6 +195,10 @@ npx @junobuild/cli init --minimal 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. +--- + +## Connecting Code and Config + 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. 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 npm run dev ``` -6. **Build the serverless functions** (in a separate terminal): +7. **Build the serverless functions** (in a separate terminal): ``` juno functions build @@ -3274,6 +3286,182 @@ These crates are used to build and extend serverless functions in Rust with Juno * [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions (e.g., `#[assert_set_doc]`, `#[on_delete_doc]`). * [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling. +# Rust Hooks Example + +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. + +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. + +You can browse the source code here: [github.com/junobuild/examples/tree/main/rust/hooks](https://github.com/junobuild/examples/tree/main/rust/hooks) + +--- + +## Folder Structure + +``` +rust/hooks/├── src/│ ├── satellite/ # Rust Satellite serverless function│ │ ├── src/│ │ │ └── lib.rs # Main Rust logic for Satellite│ │ ├── satellite.did # Candid interface definition│ │ └── Cargo.toml # Rust package config│ ├── declarations/ # TypeScript declarations for Satellite│ ├── admin.ts # Frontend admin logic│ ├── doc.ts # Frontend doc logic│ ├── main.ts # Frontend entry point│ ├── storage.ts # Frontend storage logic│ └── style.css # Frontend styles├── juno.config.ts # Juno Satellite configuration├── package.json # Frontend dependencies└── ... +``` + +--- + +## Key Features + +* **Serverless Hooks in Rust**: Demonstrates how to react to data and asset operations using hooks in Rust serverless functions. +* **Multiple Hook Types**: Includes hooks for document set, set-many, delete, and asset upload operations. +* **Serverless Integration**: Runs as a Satellite function and integrates with Juno's datastore and authentication system. +* **Minimal UI for Testing**: A simple frontend is included to test and demonstrate the hook logic in action. + +--- + +## Main Backend Components + +* **src/satellite/src/lib.rs**: The core Rust logic for the Satellite serverless function. Implements hooks for various operations (set, set-many, delete, upload). +* **src/satellite/Cargo.toml**: Rust package configuration for the Satellite function. + +--- + +## Example: Hooks in Rust + +Here’s the actual Rust logic from `lib.rs`: + +``` +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!(); +``` + +**Explanation:** + +* Defines a `Person` struct with fields for demo purposes. +* 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. +* Uses the `#[on_set_many_docs]` macro to react to batch document sets in the `demo` collection. +* Uses the `#[on_delete_doc]` macro to react to document deletions. +* Uses the `#[on_upload_asset]` macro to react to asset uploads. +* Includes a simple query endpoint (`say`) for demonstration. +* `include_satellite!();` brings in the necessary boilerplate for the Juno Satellite runtime. + +--- + +## How to Run + +1. **Clone the repo**: + +``` +git clone https://github.com/junobuild/examplescd rust/hooks +``` + +2. **Install dependencies**: + +``` +npm install +``` + +3. **Start Juno local emulator**: + +**Important:** + +Requires the Juno CLI to be available `npm i -g @junobuild/cli` + +``` +juno dev start +``` + +4. **Create a Satellite** for local dev: + +* Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. +* Update `juno.config.ts` with your Satellite ID. + +5. **Create required collections**: + +* `demo` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) +* `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) + +6. **Start the frontend dev server** (in a separate terminal): + +``` +npm run dev +``` + +7. **Build the serverless functions** (in a separate terminal): + +``` +juno functions build +``` + +The emulator will automatically upgrade your Satellite and live reload the changes. + +--- + +## Juno-Specific Configuration + +* **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. See the [Configuration reference](/docs/reference/configuration.md) for details. +* **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. + +--- + +## Production Deployment + +* Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet. +* Update `juno.config.ts` with the production Satellite ID. +* Build and deploy the frontend: + +``` +npm run buildjuno deploy +``` + +* Build and upgrade the serverless functions: + +``` +juno functions buildjuno functions upgrade +``` + +--- + +## Notes + +* This example focuses on the Rust serverless function hooks; the frontend is intentionally minimal and only included for demonstration purposes. +* Use this project as a starting point for writing custom hooks and backend logic in Rust with Juno. + +--- + +## Real-World Example + +Want to see how assertions and serverless logic are used in a live project? + +Check out [proposals.network](https://proposals.network), an open-source app built with Juno: + +* GitHub: [github.com/peterpeterparker/proposals.network](https://github.com/peterpeterparker/proposals.network) +* Example logic: [src/satellite/src/lib.rs](https://github.com/peterpeterparker/proposals.network/blob/main/src/satellite/src/lib.rs) + +This app uses: + +* `#[on_delete_doc]` and `#[assert_delete_doc]` to validate and clean up related documents and assets +* Shared helper modules like `assert`, `delete`, and `types` to keep logic organized +* A real-world pattern of chaining asset/document deletions with assertions + +It’s a great reference for more advanced setups and multi-collection coordination. + +--- + +## References + +* [Serverless Functions Guide](/docs/guides/rust.md) +* [Rust Functions Development](/docs/build/functions.md) +* [Rust SDK Reference](/docs/reference/functions/rust/sdk.md) +* [Rust Utils Reference](/docs/reference/functions/rust/utils.md) +* [Run Local Development](/docs/guides/local-development.md) +* [CLI Reference](/docs/reference/cli.md) +* [Configuration Reference](/docs/reference/configuration.md) +* [Datastore Collections](/docs/build/datastore/collections.md) + +--- + +## Crate Docs + +These crates are used to build and extend serverless functions in Rust with Juno: + +* [junobuild-satellite](https://docs.rs/junobuild-satellite): Core features and runtime for building a Satellite in Rust, including hooks, assertions, and datastore integration. +* [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions (e.g., `#[on_set_doc]`, `#[on_delete_doc]`). +* [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling. + # Using Juno with AI 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 --- +### A Satellite ID is not configured. Juno cannot be initialized. + +If you encounter the error: + +> A Satellite ID is not configured. Juno cannot be initialized. + +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. + +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) + +--- + ### Invalid character: "<" When you scaffold an app with a template, the `juno.config` file includes placeholder values for the satellite IDs: diff --git a/.llms-snapshots/llms.txt b/.llms-snapshots/llms.txt index a6077d17..800307e8 100644 --- a/.llms-snapshots/llms.txt +++ b/.llms-snapshots/llms.txt @@ -59,6 +59,7 @@ Juno is your self-contained serverless platform for building full-stack web apps ## Examples - Functions - Rust - [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. +- [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. ## Guides diff --git a/docs/examples/functions/rust/assertions.mdx b/docs/examples/functions/rust/assertions.mdx index c90003d1..2bae5072 100644 --- a/docs/examples/functions/rust/assertions.mdx +++ b/docs/examples/functions/rust/assertions.mdx @@ -1,7 +1,7 @@ --- title: Rust Assertions Example description: An example demonstrating how to write custom assertions in Rust for Juno serverless functions. -keywords: [rust, assertion, serverless, juno, satellite, example] +keywords: [rust, assertion, serverless, functions, juno, satellite, example] sidebar_label: Assertions --- @@ -140,7 +140,7 @@ juno dev start npm run dev ``` -6. **Build the serverless functions** (in a separate terminal): +7. **Build the serverless functions** (in a separate terminal): ```bash juno functions build diff --git a/docs/examples/functions/rust/hooks.mdx b/docs/examples/functions/rust/hooks.mdx new file mode 100644 index 00000000..8b143fe5 --- /dev/null +++ b/docs/examples/functions/rust/hooks.mdx @@ -0,0 +1,279 @@ +--- +title: Rust Hooks Example +description: An example demonstrating how to use serverless function hooks in Rust for Juno serverless functions. +keywords: [rust, hooks, serverless, functions, juno, satellite, example] +sidebar_label: Hooks +--- + +# Rust Hooks Example + +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. + +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. + +You can browse the source code here: [github.com/junobuild/examples/tree/main/rust/hooks](https://github.com/junobuild/examples/tree/main/rust/hooks) + +--- + +## Folder Structure + +``` +rust/hooks/ +├── src/ +│ ├── satellite/ # Rust Satellite serverless function +│ │ ├── src/ +│ │ │ └── lib.rs # Main Rust logic for Satellite +│ │ ├── satellite.did # Candid interface definition +│ │ └── Cargo.toml # Rust package config +│ ├── declarations/ # TypeScript declarations for Satellite +│ ├── admin.ts # Frontend admin logic +│ ├── doc.ts # Frontend doc logic +│ ├── main.ts # Frontend entry point +│ ├── storage.ts # Frontend storage logic +│ └── style.css # Frontend styles +├── juno.config.ts # Juno Satellite configuration +├── package.json # Frontend dependencies +└── ... +``` + +--- + +## Key Features + +- **Serverless Hooks in Rust**: Demonstrates how to react to data and asset operations using hooks in Rust serverless functions. +- **Multiple Hook Types**: Includes hooks for document set, set-many, delete, and asset upload operations. +- **Serverless Integration**: Runs as a Satellite function and integrates with Juno's datastore and authentication system. +- **Minimal UI for Testing**: A simple frontend is included to test and demonstrate the hook logic in action. + +--- + +## Main Backend Components + +- **src/satellite/src/lib.rs**: The core Rust logic for the Satellite serverless function. Implements hooks for various operations (set, set-many, delete, upload). +- **src/satellite/Cargo.toml**: Rust package configuration for the Satellite function. + +--- + +## Example: Hooks in Rust + +Here’s the actual Rust logic from `lib.rs`: + +```rust +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!(); +``` + +**Explanation:** + +- Defines a `Person` struct with fields for demo purposes. +- 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. +- Uses the `#[on_set_many_docs]` macro to react to batch document sets in the `demo` collection. +- Uses the `#[on_delete_doc]` macro to react to document deletions. +- Uses the `#[on_upload_asset]` macro to react to asset uploads. +- Includes a simple query endpoint (`say`) for demonstration. +- `include_satellite!();` brings in the necessary boilerplate for the Juno Satellite runtime. + +--- + +## How to Run + +1. **Clone the repo**: + +```bash +git clone https://github.com/junobuild/examples +cd rust/hooks +``` + +2. **Install dependencies**: + +```bash +npm install +``` + +3. **Start Juno local emulator**: + +:::important + +Requires the Juno CLI to be available `npm i -g @junobuild/cli` + +::: + +```bash +juno dev start +``` + +4. **Create a Satellite** for local dev: + +- Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. +- Update `juno.config.ts` with your Satellite ID. + +5. **Create required collections**: + +- `demo` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) +- `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) + +6. **Start the frontend dev server** (in a separate terminal): + +```bash +npm run dev +``` + +7. **Build the serverless functions** (in a separate terminal): + +```bash +juno functions build +``` + +The emulator will automatically upgrade your Satellite and live reload the changes. + +--- + +## Juno-Specific Configuration + +- **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. See the [Configuration reference](../../../reference/configuration.mdx) for details. +- **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. + +--- + +## Production Deployment + +- Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet. +- Update `juno.config.ts` with the production Satellite ID. +- Build and deploy the frontend: + +```bash +npm run build +juno deploy +``` + +- Build and upgrade the serverless functions: + +```bash +juno functions build +juno functions upgrade +``` + +--- + +## Notes + +- This example focuses on the Rust serverless function hooks; the frontend is intentionally minimal and only included for demonstration purposes. +- Use this project as a starting point for writing custom hooks and backend logic in Rust with Juno. + +--- + +## Real-World Example + +Want to see how assertions and serverless logic are used in a live project? + +Check out [proposals.network](https://proposals.network), an open-source app built with Juno: + +- GitHub: [github.com/peterpeterparker/proposals.network](https://github.com/peterpeterparker/proposals.network) +- Example logic: [src/satellite/src/lib.rs](https://github.com/peterpeterparker/proposals.network/blob/main/src/satellite/src/lib.rs) + +This app uses: + +- `#[on_delete_doc]` and `#[assert_delete_doc]` to validate and clean up related documents and assets +- Shared helper modules like `assert`, `delete`, and `types` to keep logic organized +- A real-world pattern of chaining asset/document deletions with assertions + +It’s a great reference for more advanced setups and multi-collection coordination. + +--- + +## References + +- [Serverless Functions Guide](../../../guides/rust.mdx) +- [Rust Functions Development](../../../build/functions/index.md) +- [Rust SDK Reference](../../../reference/functions/rust/sdk.mdx) +- [Rust Utils Reference](../../../reference/functions/rust/utils.mdx) +- [Run Local Development](../../../guides/local-development.mdx) +- [CLI Reference](../../../reference/cli) +- [Configuration Reference](../../../reference/configuration.mdx) +- [Datastore Collections](../../../build/datastore/collections.md) + +--- + +## Crate Docs + +These crates are used to build and extend serverless functions in Rust with Juno: + +- [junobuild-satellite](https://docs.rs/junobuild-satellite): Core features and runtime for building a Satellite in Rust, including hooks, assertions, and datastore integration. +- [junobuild-macros](https://docs.rs/junobuild-macros): Procedural macros for declaratively attaching hooks and assertions (e.g., `#[on_set_doc]`, `#[on_delete_doc]`). +- [junobuild-utils](https://docs.rs/junobuild-utils): Utility helpers for working with documents, including data encoding, decoding, and assertion context handling. diff --git a/sidebars.ts b/sidebars.ts index a02ef2b0..ef91cd6d 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -119,7 +119,10 @@ const sidebars: SidebarsConfig = { description: "Examples of writing serverless functions in Rust for Juno. Includes patterns like custom assertions, data manipulation and calls." }, - items: ["examples/functions/rust/assertions"] + items: [ + "examples/functions/rust/assertions", + "examples/functions/rust/hooks" + ] } ] }