Skip to main content

TypeScript Assertion Example

This example demonstrates how to write a custom assertion in TypeScript 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.

The project includes a minimal frontend to help trigger and test the logic, but the primary focus is the backend assertion.

You can browse the source code here: github.com/junobuild/examples/tree/main/functions/typescript/assertions


Folder Structure

typescript/assertions/
├── src/
│ ├── satellite/ # TypeScript Satellite serverless function
│ │ └── index.ts # Main TypeScript logic for Satellite
│ ├── types/
│ │ └── note.ts # Note type and schema
│ └── components/ # Minimal frontend React components
├── juno.config.ts # Juno Satellite configuration
├── package.json # Frontend and serverless dependencies
└── ...

Key Features

  • Custom Assertions in TypeScript: Demonstrates how to reject or validate data before it's saved, using TypeScript serverless functions.
  • 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 assertion logic in action.

Main Backend Components

  • src/satellite/index.ts: The core TypeScript logic for the Satellite serverless function. Implements the custom assertions (e.g., only allow certain valid inputs, etc.).
  • src/satellite/Cargo.toml: TypeScript package configuration for the Satellite function.

Example: Custom Assertion in TypeScript

Here’s the actual TypeScript logic from index.ts:

import { type AssertSetDoc, defineAssert } from "@junobuild/functions";
import { decodeDocData } from "@junobuild/functions/sdk";
import { type NoteData, NoteDataSchema } from "../types/note";

export const assertSetDoc = defineAssert<AssertSetDoc>({
collections: ["notes"],
assert: (context) => {
const note = decodeDocData<NoteData>(context.data.data.proposed.data);

NoteDataSchema.parse(note);

if (note.text.toLowerCase().includes("hello")) {
console.log("❌ Rejected note containing 'hello':", note.text);
throw new Error("The note should not contain the keyword 'hello'.");
}
}
});

Explanation:

  • Defines a NoteData type and NoteDataSchema using zod for runtime validation.
  • Uses defineAssert to create a custom assertion for the notes collection.
  • When a note is created or updated, the assertion checks if the note's text contains the word "hello" (case-insensitive).
  • If it does, the note is rejected and an error is thrown; otherwise, the note is accepted.
  • Prints a message to the log for both accepted and rejected notes.

How to Run

  1. Clone the repo:
git clone https://github.com/junobuild/examples
cd typescript/assertions

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:

  1. Create required collections:
  1. Start the frontend dev server (in a separate terminal):
npm run dev
  1. 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 for details.
  • vite.config.ts: Registers the juno plugin to inject environment variables automatically. See the Vite Plugin reference for more information.

Production Deployment

  • Create a Satellite on the Juno Console for mainnet.
  • Update juno.config.ts with the production Satellite ID.
  • Build and deploy the frontend:
npm run build
juno deploy
  • Build and upgrade the serverless functions:
juno functions build
juno functions upgrade

Notes

  • This example focuses on the TypeScript serverless function; the frontend is intentionally minimal and only included for demonstration purposes.
  • Use this project as a starting point for writing custom assertions and backend logic in TypeScript with Juno.

Real-World Example

Want to see how assertions and serverless logic are used in a live project?

Check out cycles.watch, an open-source app built with Juno:

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