From 463f41c4d94cb45f8c02921274edb71a1b48aa1f Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 11 Jul 2025 13:54:28 +0200 Subject: [PATCH 1/2] docs: example mutating docs typescript Signed-off-by: David Dal Busco --- .../functions/typescript/mutating-docs.mdx | 197 ++++++++++++++++++ sidebars.ts | 5 +- 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 docs/examples/functions/typescript/mutating-docs.mdx diff --git a/docs/examples/functions/typescript/mutating-docs.mdx b/docs/examples/functions/typescript/mutating-docs.mdx new file mode 100644 index 00000000..51bd59d5 --- /dev/null +++ b/docs/examples/functions/typescript/mutating-docs.mdx @@ -0,0 +1,197 @@ +--- +title: Mutating Documents with TypeScript Hooks +description: An example demonstrating how to modify and re-save documents in Juno Satellites using TypeScript hooks. +keywords: [typescript, hooks, serverless, functions, juno, satellite, example] +sidebar_label: Mutation +--- + +# Mutating Documents with TypeScript Hooks + +This example demonstrates how to use **hooks in TypeScript** to modify documents automatically when they're created or updated in your Juno **Satellite**. + +Hooks let you react to events like document creation, deletion, or asset uploads — and run custom backend logic in response. + +You can browse the source code here: [github.com/junobuild/examples/tree/main/functions/typescript/hooks](https://github.com/junobuild/examples/tree/main/functions/typescript/hooks) + +--- + +## Folder Structure + +``` +typescript/hooks/ +├── src/ +│ ├── satellite/ # TypeScript Satellite serverless function +│ │ ├── index.ts # Main TypeScript logic for Satellite +│ │ └── tsconfig.json # TypeScript config for Satellite +│ ├── declarations/ +│ │ └── satellite/ # 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 +│ └── types.ts # Shared types and schemas +├── juno.config.ts # Juno Satellite configuration +├── package.json # Frontend dependencies +└── ... +``` + +--- + +## Key Features + +- **Serverless Hooks in TypeScript**: Demonstrates how to react to data and asset operations using hooks in TypeScript serverless functions. +- **Multiple Hook Types**: Includes hooks for document set operations (extendable for set-many, delete, upload, etc.). +- **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/index.ts**: The core TypeScript logic for the Satellite serverless function. Implements hooks for various operations (set, assert, etc.). +- **src/types.ts**: Shared Zod schema and types for document validation and decoding. + +--- + +## Example: Mutating Documents + +Here’s the actual TypeScript logic from `index.ts`: + +```ts +import { + type AssertSetDoc, + defineAssert, + defineHook, + type OnSetDoc +} from "@junobuild/functions"; +import { PersonData, PersonDataSchema } from "../types"; +import { + decodeDocData, + encodeDocData, + setDocStore +} from "@junobuild/functions/sdk"; +import { Principal } from "@dfinity/principal"; + +export const assertSetDoc = defineAssert({ + collections: ["demo"], + assert: (context) => { + // We validate that the data submitted for create or update matches the expected schema. + const person = decodeDocData(context.data.data.proposed.data); + PersonDataSchema.parse(person); + } +}); + +export const onSetDoc = defineHook({ + collections: ["demo"], + run: async (context) => { + const { + caller, + data: { + key, + collection, + data: { after: currentDoc } + } + } = context; + + // We decode the new data saved in the Datastore because it holds those as blob. + const person = decodeDocData(currentDoc.data); + + // Some console.log for demo purpose + console.log( + "[on_set_doc] Caller:", + Principal.fromUint8Array(caller).toText() + ); + console.log("[on_set_doc] Collection:", collection); + console.log("[on_set_doc] Data:", person.principal, person.value); + + // We update the document's data that was saved in the Datastore with the call from the frontend dapp. + const { hello, ...rest } = person; + const updatePerson = { + ...rest, + hello: `${hello} checked`, + yolo: false + }; + + // We encode the data back to blob. + const updateData = encodeDocData(updatePerson); + + // We save the document for the same caller as the one who triggered the original on_set_doc, in the same collection with the same key as well. + setDocStore({ + caller: caller, + collection, + key, + doc: { + version: currentDoc.version, + data: updateData + } + }); + } +}); +``` + +**Explanation:** + +- Defines a `PersonData` type and Zod schema for validation. +- Uses `defineAssert` to validate document data before creation or update. +- Uses `defineHook` to run logic whenever a document is set in the `demo` collection. Updates the document and saves it back. +- Uses the Juno SDK for encoding/decoding and storing documents. + +--- + +## How to Run + +1. **Clone the repo**: + +```bash +git clone https://github.com/junobuild/examples +cd typescript/hooks +``` + +import HowToStart from "../../components/how-to-start.mdx"; + + + +import CreateSatellite from "../../components/create-a-satellite.mdx"; + + + +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) + +import HowToRun from "../components/how-to-run.md"; + + + +--- + +import Config from "../components/config.md"; + + + +--- + +import ProdDeploy from "../components/prod-deploy.md"; + + + +--- + +## Notes + +- This example focuses on the TypeScript serverless function. The frontend is intentionally minimal and included only for demonstration. +- Use this project as a starting point for writing custom backend logic in TypeScript using Juno hooks. + +--- + +import CyclesWatch from "./components/cycles-watch.md"; + + + +--- + +import References from "./components/references.md"; + + diff --git a/sidebars.ts b/sidebars.ts index 032c3af3..973181ff 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -138,7 +138,10 @@ const sidebars: SidebarsConfig = { description: "Examples of writing serverless functions in TypeScript for Juno. Includes patterns like custom assertions, data manipulation and calls." }, - items: ["examples/functions/typescript/assertion"] + items: [ + "examples/functions/typescript/assertion", + "examples/functions/typescript/mutating-docs" + ] } ] } From bc9369b1d3577dfd40a71073dc31b1deb75067d2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:56:02 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=84=20Update=20LLMs.txt=20snapshot?= =?UTF-8?q?=20for=20PR=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .llms-snapshots/llms-full.txt | 171 +++++++++++++++++++++++++++++++++- .llms-snapshots/llms.txt | 1 + 2 files changed, 171 insertions(+), 1 deletion(-) diff --git a/.llms-snapshots/llms-full.txt b/.llms-snapshots/llms-full.txt index 357a6836..bcf0f465 100644 --- a/.llms-snapshots/llms-full.txt +++ b/.llms-snapshots/llms-full.txt @@ -2233,7 +2233,7 @@ Write serverless backend logic for your app using TypeScript or Rust. These exam [## 🗃️ TypeScript -1 item](/docs/examples/functions/typescript.md) +2 items](/docs/examples/functions/typescript.md) # Angular Example @@ -3185,6 +3185,10 @@ Examples of writing serverless functions in TypeScript for Juno. Includes patter An example demonstrating how to write custom assertions in TypeScript for Juno serverless functions.](/docs/examples/functions/typescript/assertion.md) +[## 📄️ Mutation + +An example demonstrating how to modify and re-save documents in Juno Satellites using TypeScript hooks.](/docs/examples/functions/typescript/mutating-docs.md) + # Rust Assertion Example 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. @@ -4047,6 +4051,171 @@ It’s a great reference for more advanced setups and orchestration. * [Configuration Reference](/docs/reference/configuration.md) * [Datastore Collections](/docs/build/datastore/collections.md) +# Mutating Documents with TypeScript Hooks + +This example demonstrates how to use **hooks in TypeScript** to modify documents automatically when they're created or updated in your Juno **Satellite**. + +Hooks let you react to events like document creation, deletion, or asset uploads — and run custom backend logic in response. + +You can browse the source code here: [github.com/junobuild/examples/tree/main/functions/typescript/hooks](https://github.com/junobuild/examples/tree/main/functions/typescript/hooks) + +--- + +## Folder Structure + +``` +typescript/hooks/├── src/│ ├── satellite/ # TypeScript Satellite serverless function│ │ ├── index.ts # Main TypeScript logic for Satellite│ │ └── tsconfig.json # TypeScript config for Satellite│ ├── declarations/│ │ └── satellite/ # 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│ └── types.ts # Shared types and schemas├── juno.config.ts # Juno Satellite configuration├── package.json # Frontend dependencies└── ... +``` + +--- + +## Key Features + +* **Serverless Hooks in TypeScript**: Demonstrates how to react to data and asset operations using hooks in TypeScript serverless functions. +* **Multiple Hook Types**: Includes hooks for document set operations (extendable for set-many, delete, upload, etc.). +* **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/index.ts**: The core TypeScript logic for the Satellite serverless function. Implements hooks for various operations (set, assert, etc.). +* **src/types.ts**: Shared Zod schema and types for document validation and decoding. + +--- + +## Example: Mutating Documents + +Here’s the actual TypeScript logic from `index.ts`: + +``` +import { type AssertSetDoc, defineAssert, defineHook, type OnSetDoc} from "@junobuild/functions";import { PersonData, PersonDataSchema } from "../types";import { decodeDocData, encodeDocData, setDocStore} from "@junobuild/functions/sdk";import { Principal } from "@dfinity/principal";export const assertSetDoc = defineAssert({ collections: ["demo"], assert: (context) => { // We validate that the data submitted for create or update matches the expected schema. const person = decodeDocData(context.data.data.proposed.data); PersonDataSchema.parse(person); }});export const onSetDoc = defineHook({ collections: ["demo"], run: async (context) => { const { caller, data: { key, collection, data: { after: currentDoc } } } = context; // We decode the new data saved in the Datastore because it holds those as blob. const person = decodeDocData(currentDoc.data); // Some console.log for demo purpose console.log( "[on_set_doc] Caller:", Principal.fromUint8Array(caller).toText() ); console.log("[on_set_doc] Collection:", collection); console.log("[on_set_doc] Data:", person.principal, person.value); // We update the document's data that was saved in the Datastore with the call from the frontend dapp. const { hello, ...rest } = person; const updatePerson = { ...rest, hello: `${hello} checked`, yolo: false }; // We encode the data back to blob. const updateData = encodeDocData(updatePerson); // We save the document for the same caller as the one who triggered the original on_set_doc, in the same collection with the same key as well. setDocStore({ caller: caller, collection, key, doc: { version: currentDoc.version, data: updateData } }); }}); +``` + +**Explanation:** + +* Defines a `PersonData` type and Zod schema for validation. +* Uses `defineAssert` to validate document data before creation or update. +* Uses `defineHook` to run logic whenever a document is set in the `demo` collection. Updates the document and saves it back. +* Uses the Juno SDK for encoding/decoding and storing documents. + +--- + +## How to Run + +1. **Clone the repo**: + +``` +git clone https://github.com/junobuild/examplescd typescript/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 TypeScript serverless function. The frontend is intentionally minimal and included only for demonstration. +* Use this project as a starting point for writing custom backend logic in TypeScript using Juno hooks. + +--- + +## Real-World Example + +Want to see how assertions and serverless logic are used in a live project? + +Check out [cycles.watch](https://cycles.watch), an open-source app built with Juno: + +* GitHub: [github.com/peterpeterparker/cycles.watch](https://github.com/peterpeterparker/cycles.watch) +* Example logic: [src/satellite/index.ts](https://github.com/peterpeterparker/cycles.watch/blob/main/src/satellite/index.ts) + +This app uses: + +* `assertSetDoc` to validate requests +* `onSetDoc` to implement a swap-like feature that performs various canister calls +* Service modules to keep logic organized +* A real-world pattern for chaining calls and document insertions with assertions + +It’s a great reference for more advanced setups and orchestration. + +--- + +## References + +* [Serverless Functions Guide](/docs/guides/typescript.md) +* [Functions Development](/docs/build/functions.md) +* [TypeScript SDK Reference](/docs/reference/functions/typescript/sdk.md) +* [TypeScript ic-cdk Reference](/docs/reference/functions/typescript/ic-cdk.md) +* [TypeScript Utils Reference](/docs/reference/functions/typescript/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) + # 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. diff --git a/.llms-snapshots/llms.txt b/.llms-snapshots/llms.txt index c726e231..2ea82eff 100644 --- a/.llms-snapshots/llms.txt +++ b/.llms-snapshots/llms.txt @@ -76,6 +76,7 @@ Juno is your self-contained serverless platform for building full-stack web apps ## Examples - Functions - Typescript - [TypeScript Assertions Example](https://juno.build/docs/examples/functions/typescript/assertion.md): An example demonstrating how to write custom assertions in TypeScript for Juno serverless functions. +- [Mutating Documents with TypeScript Hooks](https://juno.build/docs/examples/functions/typescript/mutating-docs.md): An example demonstrating how to modify and re-save documents in Juno Satellites using TypeScript hooks. ## Guides