Primeiros passos com Cana
Esta página monta o menor banco Cana útil para um app de tarefas categorizadas. Ela cria duas tabelas, grava dados iniciais e lê os registros de volta.
Instalação
bun add @jumentix/canaApp mínimo completo de tarefas
import { createClient, type CanaSchema } from '@jumentix/cana';
type Category = {
id: string;
name: string;
color: string;
createdAt: number;
updatedAt: number;
};
type Task = {
id: string;
title: string;
categoryId: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
createdAt: number;
updatedAt: number;
};
const schema: CanaSchema = {
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' },
{ name: 'byUpdatedAt', keyPath: 'updatedAt' }
]
}
]
};
const client = createClient({
name: 'tasks-app',
schema,
originId: 'tasks-page'
});
await client.open();
const now = Date.now();
await client.table<Category>('categories').bulkAdd([
{ id: 'work', name: 'Work', color: '#2563eb', createdAt: now, updatedAt: now },
{ id: 'home', name: 'Home', color: '#16a34a', createdAt: now, updatedAt: now }
]);
await client.table<Task>('tasks').add({
id: 'task-1',
title: 'Write the Cana tutorial',
categoryId: 'work',
completed: false,
priority: 'high',
createdAt: now,
updatedAt: now
});
const categories = await client.table<Category>('categories').query({ index: 'byName' });
const tasks = await client.table<Task>('tasks').query({ index: 'byUpdatedAt' });
console.log({ backend: client.backend, categories, tasks });O que observar
open()é explícito. O Cana não esconde upgrade de schema atrás de umget()ouput()sem relação.client.backendinforma qual backend de storage abriu, útil para diagnóstico.- Nomes de tabela são strings porque atravessam fronteiras de worker e
IndexedDB. Mantenha nomes estáveis e pequenos:
categories,tasks. - Estado de UI não fica no Cana. Guarde estado renderizado em React, Redux, Pinia ou outra camada de UI, e grave dados duráveis pelo Cana.
Execute aqui
Primeiros passos
Abra um client, crie registros Category e Task, depois leia de volta.
### Primeiros passos
```ts
const client = cana.createClient({
name: dbName,
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' },
{ name: 'byUpdatedAt', keyPath: 'updatedAt' }
]
}
]
}
});
await client.open();
await client.table('categories').add({
id: 'work',
name: 'Work',
color: '#2563eb',
createdAt: Date.now(),
updatedAt: Date.now()
});
await client.table('tasks').add({
id: 'task-1',
title: 'Write the Cana tutorial',
categoryId: 'work',
completed: false,
priority: 'high',
createdAt: Date.now(),
updatedAt: Date.now()
});
return {
backend: client.backend,
category: await client.table('categories').get('work'),
task: await client.table('tasks').get('task-1')
};
```const client = cana.createClient({
name: dbName,
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' },
{ name: 'byUpdatedAt', keyPath: 'updatedAt' }
]
}
]
}
});
await client.open();
await client.table('categories').add({
id: 'work',
name: 'Work',
color: '#2563eb',
createdAt: Date.now(),
updatedAt: Date.now()
});
await client.table('tasks').add({
id: 'task-1',
title: 'Write the Cana tutorial',
categoryId: 'work',
completed: false,
priority: 'high',
createdAt: Date.now(),
updatedAt: Date.now()
});
return {
backend: client.backend,
category: await client.table('categories').get('work'),
task: await client.table('tasks').get('task-1')
};Próximo
Continue em schema e chaves antes de adicionar mais tabelas.