Storage, Tasks e Plugins
Aula 3 de 4
Storage
Nitro possui um sistema de storage unificado que funciona em múltiplos ambientes.
Configuração
// nitro.config.ts
import { defineNitroConfig } from 'nitro/config';
export default defineNitroConfig({
storage: {
// Storage local (filesystem)
'data': {
driver: 'fs',
base: './data'
},
// Cache em Redis
'cache': {
driver: 'redis',
host: process.env.REDIS_HOST || 'localhost',
port: 6379,
ttl: 3600
},
// Cloudflare KV (quando deploy no Cloudflare)
'kv': {
driver: 'cloudflare-kv-binding',
binding: 'MY_KV'
}
}
});
Uso do Storage
// server/api/data.ts
export default defineEventHandler(async (event) => {
const storage = useStorage('data');
// Escrever
await storage.setItem('users:1', {
name: 'Alice',
email: '[email protected]'
});
await storage.setItem('visits', 0, { ttl: 3600 });
// Ler
const user = await storage.getItem('users:1');
const visits = await storage.getItem('visits');
// Listar
const keys = await storage.getKeys('users:');
// Remover
await storage.removeItem('users:1');
// Limpar
await storage.clear();
return { user };
});
Storage Filesystem
// server/api/notes.ts
export default defineEventHandler(async (event) => {
const storage = useStorage('data');
switch (event.method) {
case 'GET': {
const notes = await storage.getKeys('notes:');
const data = await Promise.all(
notes.map(async (key) => ({
id: key.split(':')[1],
...(await storage.getItem(key))
}))
);
return data;
}
case 'POST': {
const body = await readBody(event);
const id = Date.now().toString();
await storage.setItem(`notes:${id}`, {
title: body.title,
content: body.content,
createdAt: new Date().toISOString()
});
return { id, ...body };
}
}
});
Storage no Filesystem
# Estrutura criada em ./data/
data/
├── notes:1700000000000.json
├── notes:1700000000001.json
└── users:1.json
Scheduled Tasks (Cron)
Nitro suporta tarefas agendadas que executam em intervalos definidos.
// server/tasks/cleanup.ts
export default defineTask({
meta: {
name: 'cleanup',
description: 'Limpa dados antigos'
},
async run({ payload }) {
console.log('Running cleanup task...');
const storage = useStorage('data');
const keys = await storage.getKeys('temp:');
for (const key of keys) {
const item = await storage.getItem(key);
if (item && item.expiresAt < Date.now()) {
await storage.removeItem(key);
}
}
return { result: `Cleaned ${keys.length} items` };
}
});
// server/tasks/sync.ts
export default defineTask({
meta: {
name: 'sync-external',
description: 'Sincroniza dados com API externa'
},
async run({ payload }) {
const response = await fetch('https://api.externa.com/data');
const data = await response.json();
const storage = useStorage('data');
await storage.setItem('synced-data', data);
return { result: 'Sync completed', records: data.length };
}
});
Configuração de Cron
// nitro.config.ts
export default defineNitroConfig({
scheduledTasks: {
// Executa a cada 1 hora
'cleanup': '0 * * * *',
// Executa todo dia às 3:00 AM
'sync-external': '0 3 * * *',
// A cada 5 minutos
'health-check': '*/5 * * * *'
}
});
Executar Tarefa Manualmente
// server/api/tasks/[name].post.ts
export default defineEventHandler(async (event) => {
const name = getRouterParam(event, 'name');
if (!name) {
throw createError({ statusCode: 400, message: 'Task name is required' });
}
const result = await runTask(name);
return result;
});
curl -X POST http://localhost:3000/api/tasks/cleanup
Server Plugins
Plugins permitem executar código na inicialização do servidor e adicionar hooks.
// server/plugins/database.ts
export default defineNitroPlugin(async (nitroApp) => {
console.log('Inicializando conexão com banco...');
// Conectar ao banco
const db = await connectToDatabase();
// Tornar disponível globalmente
nitroApp.db = db;
// Hook de requisição
nitroApp.hooks.hook('request', (event) => {
event.context.db = db;
});
// Hook de erro
nitroApp.hooks.hook('error', (error, { event }) => {
console.error('Erro na aplicação:', error);
});
});
// server/plugins/security.ts
export default defineNitroPlugin((nitroApp) => {
// Adicionar headers de segurança globalmente
nitroApp.hooks.hook('render:response', (response, { event }) => {
response.headers = {
...response.headers,
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'Referrer-Policy': 'strict-origin-when-cross-origin'
};
});
});
Hooks do Nitro
// server/plugins/hooks.ts
export default defineNitroPlugin((nitroApp) => {
// Disparado antes de cada requisição
nitroApp.hooks.hook('request', (event) => {
event.context.startTime = Date.now();
});
// Disparado após response ser enviada
nitroApp.hooks.hook('afterResponse', (event, { response }) => {
const duration = Date.now() - event.context.startTime;
console.log(`${event.method} ${event.path} - ${duration}ms`);
});
// Disparado quando um erro ocorre
nitroApp.hooks.hook('error', (error, { event }) => {
// Enviar para Sentry/Datadog
console.error(`Error in ${event.path}:`, error);
});
// Disparado antes de renderizar HTML
nitroApp.hooks.hook('render:response', (response, { event }) => {
// Modificar response antes de enviar
});
});
Runtime Config
// nitro.config.ts
export default defineNitroConfig({
runtimeConfig: {
appVersion: '1.0.0',
public: {
siteUrl: process.env.SITE_URL || 'http://localhost:3000'
},
private: {
apiKey: process.env.API_KEY,
databaseUrl: process.env.DATABASE_URL
}
}
});
// server/api/config.ts
export default defineEventHandler(async () => {
const config = useRuntimeConfig(event);
return {
version: config.appVersion,
siteUrl: config.public.siteUrl,
// Não expor config.private!
};
});
Lab: Exercício - Sistema de Cache e Tasks
// nitro.config.ts
export default defineNitroConfig({
storage: {
'cache': {
driver: 'fs',
base: './cache'
}
},
scheduledTasks: {
'clean-cache': '0 */6 * * *' // a cada 6 horas
},
runtimeConfig: {
cacheTtl: 300
}
});
// server/tasks/clean-cache.ts
export default defineTask({
meta: { name: 'clean-cache', description: 'Limpa cache expirado' },
async run() {
const storage = useStorage('cache');
const keys = await storage.getKeys('');
let removed = 0;
for (const key of keys) {
const item = await storage.getItem(key);
if (item && item.ttl && item.ttl < Date.now()) {
await storage.removeItem(key);
removed++;
}
}
return { result: `Removed ${removed} expired cache entries` };
}
});
# Executar task manualmente
curl -X POST http://localhost:3000/api/tasks/clean-cache
Storage do Nitro é unificado e portável entre ambientes (fs, redis, cloudflare KV). Scheduled tasks com cron automatizam operações. Plugins permitem hooks globais. Runtime config separa config pública e privada.