Domine ElysiaJS: aprenda a criar servidor em Bun.js com middlewares essenciais (CORS, CSRF, Autenticação JWT). Guia prático para a fundação do seu projeto Restaurantix.
ElysiaJS é um framework web ultra-rápido para Bun.js que oferece várias vantagens:
Construído especificamente para Bun.js, aproveitando todas as otimizações nativas. Até 10x mais rápido que Express.js em benchmarks.
TypeScript nativo com validação automática de schemas. Erros detectados em tempo de compilação, não runtime.
Sintaxe limpa e intuitiva. Middlewares como plugins reutilizáveis. Decoradores para injeção de dependências.
Plugins oficiais para JWT, CORS, Swagger, WebSocket. Integração nativa com Eden Treaty para client-side.
ElysiaJS usa um sistema de plugins que são compostos de forma funcional:
// Cada plugin é uma instância de Elysia
const authPlugin = new Elysia({ name: "auth" })
.derive(() => ({ user: getCurrentUser() })) // Injeta dependências
.macro(() => ({ isSignedIn: true })) // Define decoradores
// Plugins são compostos
const app = new Elysia()
.use(corsPlugin) // Middleware global
.use(authPlugin) // Plugin de autenticação
.get("/", handler) // Rota específica
🔄 Pipeline de Requisição: Request → Parse → Transform → Before Handle → Route Handler → After Handle → Response
Drizzle ORM é a escolha perfeita para TypeScript moderno:
Define seu schema uma vez e obtém tipos TypeScript automaticamente. Migrações geradas automaticamente.
Sem overhead de runtime. SQL puro gerado em tempo de build. Performance superior a ORMs tradicionais.
Queries completamente tipadas. Erros de SQL detectados em tempo de compilação. Auto-complete perfeito no editor.
API familiar para quem conhece SQL. Queries complexas simples de escrever. Suporte completo a relacionamentos.
Drizzle usa um sistema de schema declarativo que gera tipos TypeScript:
// Schema definido uma vez
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }).unique(),
createdAt: timestamp('created_at').defaultNow()
});
// Tipos gerados automaticamente
type User = typeof users.$inferSelect; // { id: number, name: string, ... }
type NewUser = typeof users.$inferInsert; // { name: string, email?: string, ... }
// Queries tipadas
const user = await db.select().from(users).where(eq(users.id, 1));
Com drizzle-typebox, podemos usar schemas Drizzle diretamente na validação:
import { createInsertSchema } from 'drizzle-typebox';
// Schema Drizzle vira validação Elysia
const createUserSchema = createInsertSchema(users, {
email: t.String({ format: 'email' }) // Refinamento customizado
});
new Elysia()
.post('/users', ({ body }) => {
// body já é tipado como NewUser
return db.insert(users).values(body);
}, {
body: t.Omit(createUserSchema, 'id') // Remove campos auto-gerados
});
🎯 Type Safety Completa: Schema → Validação → Handler → Response
Uma mudança no banco reflete automaticamente em toda a aplicação!
Primeiro, vamos criar a estrutura de pastas do nosso projeto:
mkdir restaurantix
cd restaurantix
bun init -y
# Estrutura de pastas
mkdir -p src/http/routes
mkdir -p src/application/infra/db
touch src/http/server.ts
touch src/http/auth.ts
touch src/http/csrf.ts
bun add elysia @elysiajs/cors @elysiajs/jwt @elysiajs/cookie
bun add drizzle-orm drizzle-kit postgres
bun add @paralleldrive/cuid2 dayjs resend zod
bun add -d @types/pg
Vamos criar o middleware de proteção CSRF:
// src/http/csrf.ts
import { Elysia } from "elysia";
import { cookie } from "@elysiajs/cookie";
import { createId } from "@paralleldrive/cuid2";
export const csrf = new Elysia({ name: "csrf" })
.use(cookie())
.derive(({ cookie, set }) => {
return {
csrfToken: () => {
if (!cookie.csrfToken) {
const token = createId();
cookie.csrfToken = {
value: token,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
maxAge: 60 * 60 * 24 * 7, // 7 days
};
return token;
}
return cookie.csrfToken.value;
}
};
})
.macro(({ onBeforeHandle }) => ({
csrf: (enabled: boolean = true) => {
if (!enabled) return;
onBeforeHandle(({ cookie, headers, error }) => {
const token = cookie.csrfToken?.value;
const headerToken = headers["x-csrf-token"];
if (!token || token !== headerToken) {
return error(403, "Invalid CSRF token");
}
});
}
}));
Agora o middleware de autenticação JWT:
// src/http/auth.ts
import { Elysia } from "elysia";
import { jwt } from "@elysiajs/jwt";
import { cookie } from "@elysiajs/cookie";
export const auth = new Elysia({ name: "auth" })
.use(
jwt({
name: "jwt",
secret: process.env.JWT_SECRET || "your-secret-key"
})
)
.use(cookie())
.derive(({ jwt, cookie }) => {
return {
getCurrentUser: async () => {
const token = cookie.auth;
if (!token) {
throw new Error("Unauthorized");
}
const payload = await jwt.verify(token);
if (!payload) {
throw new Error("Unauthorized");
}
return payload as { sub: string };
},
signUser: async (userId: string) => {
return await jwt.sign({ sub: userId });
}
};
})
.macro(({ onBeforeHandle }) => ({
isSignedIn: (enabled: boolean = true) => {
if (!enabled) return;
onBeforeHandle(async ({ getCurrentUser, error }) => {
try {
await getCurrentUser();
} catch {
return error(401, "Unauthorized");
}
});
}
}));
Agora vamos criar o servidor principal com os middlewares:
// src/http/server.ts
import { Elysia } from "elysia";
import { cors } from "@elysiajs/cors";
import { auth } from "./auth";
import { csrf } from "./csrf";
const app = new Elysia()
.use(cors())
.use(csrf)
.use(auth)
.get("/", () => ({ message: "Restaurantix API" }))
.onError(({ error, code, set }) => {
console.error(error);
switch (code) {
case "VALIDATION":
set.status = error.status;
return error.toResponse();
default:
set.status = 500;
console.error(error);
return new Response(null, { status: 500 });
}
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
# Executar o servidor
bun run src/http/server.ts
# Testar em outro terminal
curl http://localhost:3000
# Resposta: {"message":"Restaurantix API"}
Na próxima aula, vamos começar a adicionar as rotas do sistema, começando pelas rotas de autenticação e CSRF token.
💡 Dica: Os middlewares são executados na ordem que são registrados. CORS primeiro, depois CSRF, depois Auth.
✅ Checkpoint: Você agora tem um servidor ElysiaJS funcionando com middlewares de segurança configurados!