Schema e chaves
O schema do Cana é dado puro. O IndexedDB aplica mudanças de schema somente quando a versão do banco aumenta, então versionamento faz parte do contrato da aplicação.
Schema versionado
import { createClient, validateSchema, type CanaSchema } from '@jumentix/cana';
const schemaV1: CanaSchema = {
version: 1,
stores: [
{
name: 'categories',
keyPath: 'id',
indexes: [{ name: 'byName', keyPath: 'name', unique: true }]
}
]
};
const schemaV2: CanaSchema = {
version: 2,
stores: [
{
name: 'categories',
keyPath: 'id',
indexes: [{ name: 'byName', keyPath: 'name', unique: true }]
},
{
name: 'tasks',
keyPath: 'id',
indexes: [
{ name: 'byCategory', keyPath: 'categoryId' },
{ name: 'byUpdatedAt', keyPath: 'updatedAt' }
]
}
]
};
const problems = validateSchema(schemaV2);
if (problems.length > 0) {
throw new Error(problems.join('\n'));
}
const firstClient = createClient({ name: 'tasks-schema-demo', schema: schemaV1 });
await firstClient.open();
await firstClient.table('categories').put({ id: 'work', name: 'Work' });
await firstClient.close();
const upgradedClient = createClient({ name: 'tasks-schema-demo', schema: schemaV2 });
await upgradedClient.open();
await upgradedClient.table('tasks').put({
id: 'task-1',
title: 'Created after schema upgrade',
categoryId: 'work',
completed: false,
priority: 'medium',
createdAt: Date.now(),
updatedAt: Date.now()
});Regras de upgrade
| Mudança | Comportamento do Cana |
|---|---|
| Adicionar store | Aplicado na próxima versão maior. |
| Adicionar índice | Aplicado na próxima versão maior. |
| Remover uma store do objeto de schema | A store antiga permanece. O Cana evita perda acidental de dados. |
| Alterar definição de índice existente | O índice antigo permanece. Use um novo nome de índice para reconstruir de propósito. |
| Abrir versão antiga sobre dados novos | Recusado com UpgradeFailed. |
Estratégias de chave
const inboundSchema: CanaSchema = {
version: 1,
stores: [{ name: 'tasks', keyPath: 'id' }]
};
const generatedInboundSchema: CanaSchema = {
version: 1,
stores: [{ name: 'tasks', keyPath: 'id', autoIncrement: true }]
};
const outboundSchema: CanaSchema = {
version: 1,
stores: [{ name: 'cache' }]
};Use chaves inbound para registros de aplicação como categories e tasks.
Use chaves outbound para entradas de cache em que a chave é metadado fora do
objeto armazenado.
await client.table('tasks').put({
id: 'task-1',
title: 'Stable inbound key',
categoryId: 'work',
completed: false,
priority: 'medium',
createdAt: 1,
updatedAt: 1
});
await client.table('cache').put(
{ body: '<html>cached response</html>', savedAt: Date.now() },
'https://example.com/tasks'
);Execute aqui
Upgrade de schema
Comece com Category, depois suba a versao e adicione a tabela Task.
### Upgrade de schema
```ts
const v1 = cana.createClient({
name: dbName,
schema: {
version: 1,
stores: [{ name: 'categories', keyPath: 'id' }]
}
});
await v1.open();
await v1.table('categories').add({ id: 'work', name: 'Work' });
await v1.close();
const v2 = 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 v2.open();
await v2.table('tasks').add({
id: 'task-1',
title: 'Created after upgrade',
categoryId: 'work',
completed: false,
priority: 'medium',
createdAt: Date.now(),
updatedAt: Date.now()
});
return {
categories: await v2.table('categories').query(),
tasks: await v2.table('tasks').query()
};
```const v1 = cana.createClient({
name: dbName,
schema: {
version: 1,
stores: [{ name: 'categories', keyPath: 'id' }]
}
});
await v1.open();
await v1.table('categories').add({ id: 'work', name: 'Work' });
await v1.close();
const v2 = 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 v2.open();
await v2.table('tasks').add({
id: 'task-1',
title: 'Created after upgrade',
categoryId: 'work',
completed: false,
priority: 'medium',
createdAt: Date.now(),
updatedAt: Date.now()
});
return {
categories: await v2.table('categories').query(),
tasks: await v2.table('tasks').query()
};Chaves
Use ids estaveis nos registros Category e Task.
### Chaves
```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: 'docs',
name: 'Docs',
color: '#0f766e',
createdAt: 1,
updatedAt: 1
});
await client.table('tasks').add({
id: 'docs-1',
title: 'Document stable keys',
categoryId: 'docs',
completed: false,
priority: 'medium',
createdAt: 2,
updatedAt: 2
});
return {
categoryKey: 'docs',
taskKey: 'docs-1',
task: await client.table('tasks').get('docs-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: 'docs',
name: 'Docs',
color: '#0f766e',
createdAt: 1,
updatedAt: 1
});
await client.table('tasks').add({
id: 'docs-1',
title: 'Document stable keys',
categoryId: 'docs',
completed: false,
priority: 'medium',
createdAt: 2,
updatedAt: 2
});
return {
categoryKey: 'docs',
taskKey: 'docs-1',
task: await client.table('tasks').get('docs-1')
};Próximo
Continue em leitura, escrita e operações em lote.