🍡 mochi

SSR framework for Svelte 5 + Bun with islands-based selective hydration

On this page

Utility helpers

Small functions exported from mochi-framework for shaping responses and form-action results. Each helper is documented in depth where it is used; this page is a single index.

Response helpers

json(data, init?): build a JSON Response with the right Content-Type. Use from Mochi.api() handlers and middleware.

import { json } from 'mochi-framework';

return json({ ok: true }, { status: 201 });

error(status, message): throw a MochiHttpError that the framework catches and renders as the configured error page (or a JSON envelope from API routes). See Error handling.

import { error } from 'mochi-framework';

if (!user) error(404, 'User not found');

apiError(status, message): return — don’t throw — a JSON error Response shaped as { error: { message, status } }. Use inside Mochi.api() when you want a typed error without unwinding the stack. See API routes.

import { apiError } from 'mochi-framework';

return apiError(400, 'Missing id');

Form-action helpers

Used as return values from a Mochi.page action. See Defining routes and Progressive enhancement for the full action lifecycle.

fail(status, data): re-render the page with form = { ok: false, ...data } and the given HTTP status. Use for validation errors.

import { fail } from 'mochi-framework';

if (!username) return fail(400, { error: 'Username required', username });

success(data?): re-render the page with form = { ok: true, ...data } and HTTP 200. Use when the action completes but you want to stay on the page.

import { success } from 'mochi-framework';

return success({ message: 'Saved.' });

redirect(status, location): issue an HTTP redirect after the action runs. status must be 301, 302, 303, 307, or 308. Use 303 for the standard POST/Redirect/GET pattern.

import { redirect } from 'mochi-framework';

return redirect(303, '/dashboard');

Sealed tokens

encryptPayload(plaintext, { aad?, compress? }) / decryptPayload(token, { aad? }): seal a string into an opaque, tamper-proof base64url token (AES-256-SIV keyed from MOCHI_KEY) and open it again. decryptPayload returns null on any tamper or aad mismatch. This is the same primitive Mochi uses for server-island props — use it for short-lived signed values like form challenges or magic links.

import { encryptPayload, decryptPayload } from 'mochi-framework';

const token = encryptPayload(JSON.stringify({ iat: Date.now() }), { aad: 'my-form' });
const opened = decryptPayload(token, { aad: 'my-form' }); // string | null

See it in action

Live demos showing key concepts from this page