Two local tables
Category and Task prove relationship, filtering, and event-driven UI updates.
Data slicePair generated SDK clients with a SPA or PWA architecture, IndexedDB persistence, and the same contract vocabulary as the backend.

bun install
bun run cli
bun run dev:expressFrom zero to first MVP
Use this path when the first MVP must run in the browser, keep local state, and later synchronize with backend contracts.
Start with Category and Task tables, one filter, one create action, and one event the UI can listen to.
MVP output: browser data model with a visible workflow.
const schema = {
version: 1,
stores: [
{ name: 'categories', keyPath: 'id', indexes: [{ name: 'byName', keyPath: 'name', unique: true }] },
{
name: 'tasks',
keyPath: 'id',
indexes: [
{ name: 'byCategory', keyPath: 'categoryId' },
{ name: 'byCompleted', keyPath: 'completed' }
]
}
]
} as const;MVP scope
Category and Task prove relationship, filtering, and event-driven UI updates.
Data sliceUse Context, Redux, or Pinia as the first integration surface.
UI sliceDocument whether the MVP is local-only, sync-later, or API-backed.
Product slice| Proof area | Question to answer | Jumentix mechanism | MVP evidence |
|---|---|---|---|
| Local data | Can the app create and read records with no server? | Cana/browser in-memory playground and local persistence checks. | The first workflow works offline. |
| State updates | Do components refresh from Cana events? | React/Vue state-management examples and event listener assertions. | UI stays consistent with local data. |
| Future backend | Can the same vocabulary map to API contracts later? | Generated SDK names and Category/Task contract parity. | The frontend MVP does not invent a separate domain. |
Practical implementation
These examples keep Category and Task as the product vocabulary and show the controller, contract, client, worker, or state layer needed to reach a runnable MVP.
type Category = { id: string; name: string };
type Task = { id: string; title: string; categoryId: string; completed: boolean };
const categories = new Map<string, Category>([
['work', { id: 'work', name: 'Work' }]
]);
const tasks = new Map<string, Task>();
const listeners = new Set<() => void>();
export function subscribe(listener: () => void) {
listeners.add(listener);
return () => listeners.delete(listener);
}
export function createTask(input: { title: string; categoryId: string }) {
if (!categories.has(input.categoryId)) throw new Error('category not found');
const task = { id: crypto.randomUUID(), completed: false, ...input };
tasks.set(task.id, task);
listeners.forEach((listener) => listener());
return task;
}
export function listTaskCards() {
return [...tasks.values()].map((task) => ({
...task,
category: categories.get(task.categoryId)
}));
}Keep exploring
Ship OpenAPI 3.1 services with interchangeable native HTTP adapters.
Explore the blueprintRun Socket.IO or gRPC beside a REST fallback and AsyncAPI documentation.
Explore the blueprintLaunch one deployable with domain boundaries ready to become services.
Explore the blueprintKeep service communication contract-based with the Message Mediator.
Explore the blueprintBuild frontend products that share generated SDKs and work offline.
Explore the blueprintExplore the source, run the factory locally, and turn your next Node.js service into a repeatable platform capability.