Nesta aula vamos criar a estrutura base do servidor ElysiaJS com os middlewares essenciais: CORS, CSRF e Autenticação. Esta é a fundação do projeto Restaurantix.
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!