AzuraJS Logo
AzuraJSFramework
v2.2 Beta

Exemplos

Exemplos práticos e completos de aplicações AzuraJS

Exemplos 📚

Aprenda com exemplos práticos e completos de aplicações reais construídas com AzuraJS.

API REST Completa 🎯

Estrutura do Projeto

src/
  controllers/
    UserController.ts
    PostController.ts
  models/
    User.ts
    Post.ts
  middleware/
    auth.ts
    errorHandler.ts
  config/
    database.ts
  server.ts

User Model

// models/User.ts
export interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
  updatedAt: Date;
}

export interface CreateUserDto {
  name: string;
  email: string;
  password: string;
}

export interface UpdateUserDto {
  name?: string;
  email?: string;
}

User Controller

TypeScript

// controllers/UserController.ts
import { Controller, Get, Post, Put, Delete, Body, Param, Res } from "azurajs/decorators";
import { HttpError } from "azurajs/http-error";
import { hashPassword, comparePassword } from "../utils/crypto";

@Controller("/api/users")
export class UserController {
  @Get()
  async getAllUsers() {
    const users = await db.query("SELECT id, name, email, created_at FROM users");
    return { users };
  }

  @Get("/:id")
  async getUser(@Param("id") id: string) {
    const user = await db.query(
      "SELECT id, name, email, created_at FROM users WHERE id = $1",
      [id]
    );
    
    if (!user) {
      throw new HttpError(404, "Usuário não encontrado");
    }
    
    return { user };
  }

  @Post()
  async createUser(@Body() data: CreateUserDto, @Res() res: ResponseServer) {
    // Validar dados
    if (!data.email || !data.password) {
      throw new HttpError(400, "Email e senha são obrigatórios");
    }
    
    // Verificar se email já existe
    const exists = await db.query("SELECT id FROM users WHERE email = $1", [data.email]);
    if (exists) {
      throw new HttpError(409, "Email já está em uso");
    }
    
    // Hash da senha
    const hashedPassword = await hashPassword(data.password);
    
    // Criar usuário
    const user = await db.query(
      "INSERT INTO users (name, email, password) VALUES ($1, $2, $3) RETURNING id, name, email, created_at",
      [data.name, data.email, hashedPassword]
    );
    
    res.status(201).json({ user });
  }

  @Put("/:id")
  async updateUser(@Param("id") id: string, @Body() data: UpdateUserDto) {
    const user = await db.query(
      "UPDATE users SET name = COALESCE($1, name), email = COALESCE($2, email), updated_at = NOW() WHERE id = $3 RETURNING id, name, email",
      [data.name, data.email, id]
    );
    
    if (!user) {
      throw new HttpError(404, "Usuário não encontrado");
    }
    
    return { user };
  }

  @Delete("/:id")
  async deleteUser(@Param("id") id: string, @Res() res: ResponseServer) {
    const result = await db.query("DELETE FROM users WHERE id = $1", [id]);
    
    if (result.rowCount === 0) {
      throw new HttpError(404, "Usuário não encontrado");
    }
    
    res.status(204).end();
  }
}

JavaScript

// controllers/UserController.js
const { HttpError } = require("azurajs/http-error");
const { hashPassword } = require("../utils/crypto");

class UserController {
  async getAllUsers(req, res) {
    const users = await db.query("SELECT id, name, email, created_at FROM users");
    res.json({ users });
  }

  async getUser(req, res) {
    const { id } = req.params;
    const user = await db.query(
      "SELECT id, name, email, created_at FROM users WHERE id = $1",
      [id]
    );
    
    if (!user) {
      throw new HttpError(404, "Usuário não encontrado");
    }
    
    res.json({ user });
  }

  async createUser(req, res) {
    const data = req.body;
    
    // Validar dados
    if (!data.email || !data.password) {
      throw new HttpError(400, "Email e senha são obrigatórios");
    }
    
    // Verificar se email já existe
    const exists = await db.query("SELECT id FROM users WHERE email = $1", [data.email]);
    if (exists) {
      throw new HttpError(409, "Email já está em uso");
    }
    
    // Hash da senha
    const hashedPassword = await hashPassword(data.password);
    
    // Criar usuário
    const user = await db.query(
      "INSERT INTO users (name, email, password) VALUES ($1, $2, $3) RETURNING id, name, email, created_at",
      [data.name, data.email, hashedPassword]
    );
    
    res.status(201).json({ user });
  }

  async updateUser(req, res) {
    const { id } = req.params;
    const data = req.body;
    
    const user = await db.query(
      "UPDATE users SET name = COALESCE($1, name), email = COALESCE($2, email), updated_at = NOW() WHERE id = $3 RETURNING id, name, email",
      [data.name, data.email, id]
    );
    
    if (!user) {
      throw new HttpError(404, "Usuário não encontrado");
    }
    
    res.json({ user });
  }

  async deleteUser(req, res) {
    const { id } = req.params;
    const result = await db.query("DELETE FROM users WHERE id = $1", [id]);
    
    if (result.rowCount === 0) {
      throw new HttpError(404, "Usuário não encontrado");
    }
    
    res.status(204).end();
  }
}

// Registrar rotas
const controller = new UserController();
app.get("/api/users", controller.getAllUsers);
app.get("/api/users/:id", controller.getUser);
app.post("/api/users", controller.createUser);
app.put("/api/users/:id", controller.updateUser);
app.delete("/api/users/:id", controller.deleteUser);

module.exports = { UserController };

Autenticação com JWT 🔐

Auth Controller

// controllers/AuthController.ts
import { Controller, Post, Get, Body, Headers, Res } from "azurajs/decorators";
import { HttpError } from "azurajs/http-error";
import type { ResponseServer } from "azurajs";
import { sign, verify } from "jsonwebtoken";

const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key";

@Controller("/auth")
export class AuthController {
  @Post("/register")
  async register(@Body() data: CreateUserDto, @Res() res: ResponseServer) {
    // Validar dados
    if (!data.email || !data.password) {
      throw new HttpError(400, "Email e senha são obrigatórios");
    }
    
    // Verificar se usuário existe
    const exists = await db.query("SELECT id FROM users WHERE email = $1", [data.email]);
    if (exists) {
      throw new HttpError(409, "Email já está em uso");
    }
    
    // Hash da senha
    const hashedPassword = await hashPassword(data.password);
    
    // Criar usuário
    const user = await db.query(
      "INSERT INTO users (name, email, password) VALUES ($1, $2, $3) RETURNING id, name, email",
      [data.name, data.email, hashedPassword]
    );
    
    // Gerar JWT
    const token = sign(
      { userId: user.id, email: user.email },
      JWT_SECRET,
      { expiresIn: "7d" }
    );
    
    res.status(201).json({ user, token });
  }

  @Post("/login")
  async login(@Body() credentials: { email: string; password: string }) {
    // Buscar usuário
    const user = await db.query(
      "SELECT id, name, email, password FROM users WHERE email = $1",
      [credentials.email]
    );
    
    if (!user) {
      throw new HttpError(401, "Credenciais inválidas");
    }
    
    // Verificar senha
    const valid = await comparePassword(credentials.password, user.password);
    if (!valid) {
      throw new HttpError(401, "Credenciais inválidas");
    }
    
    // Gerar JWT
    const token = sign(
      { userId: user.id, email: user.email },
      JWT_SECRET,
      { expiresIn: "7d" }
    );
    
    return {
      user: { id: user.id, name: user.name, email: user.email },
      token
    };
  }

  @Get("/me")
  async getCurrentUser(@Headers("authorization") auth: string) {
    if (!auth) {
      throw new HttpError(401, "Token não fornecido");
    }
    
    const token = auth.replace("Bearer ", "");
    
    try {
      const decoded = verify(token, JWT_SECRET) as any;
      
      const user = await db.query(
        "SELECT id, name, email FROM users WHERE id = $1",
        [decoded.userId]
      );
      
      if (!user) {
        throw new HttpError(404, "Usuário não encontrado");
      }
      
      return { user };
    } catch (error) {
      throw new HttpError(401, "Token inválido");
    }
  }
}

Auth Middleware

// middleware/auth.ts
import { verify } from "jsonwebtoken";
import type { RequestServer, ResponseServer } from "azurajs";

const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key";

export function authMiddleware(
  req: RequestServer,
  res: ResponseServer,
  next: () => void
) {
  const auth = req.headers.authorization;
  
  if (!auth) {
    return res.status(401).json({ error: "Token não fornecido" });
  }
  
  const token = auth.replace("Bearer ", "");
  
  try {
    const decoded = verify(token, JWT_SECRET) as any;
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: "Token inválido" });
  }
}

Upload de Arquivos 📤

import { Controller, Post, Req, Res } from "azurajs/decorators";
import { HttpError } from "azurajs/http-error";
import type { RequestServer, ResponseServer } from "azurajs";
import { writeFile } from "fs/promises";
import { randomUUID } from "crypto";

@Controller("/api/uploads")
export class UploadController {
  @Post("/image")
  async uploadImage(@Req() req: RequestServer, @Res() res: ResponseServer) {
    // Verificar Content-Type
    const contentType = req.headers["content-type"];
    if (!contentType?.includes("multipart/form-data")) {
      throw new HttpError(400, "Content-Type deve ser multipart/form-data");
    }
    
    // Parsear multipart data (exemplo simplificado)
    const boundary = contentType.split("boundary=")[1];
    const parts = parseMultipart(req.body, boundary);
    
    const file = parts.find(p => p.name === "file");
    if (!file) {
      throw new HttpError(400, "Arquivo não encontrado");
    }
    
    // Validar tipo de arquivo
    if (!file.contentType.startsWith("image/")) {
      throw new HttpError(400, "Apenas imagens são permitidas");
    }
    
    // Validar tamanho (máximo 5MB)
    if (file.data.length > 5 * 1024 * 1024) {
      throw new HttpError(400, "Arquivo muito grande (máximo 5MB)");
    }
    
    // Salvar arquivo
    const filename = `${randomUUID()}.${file.extension}`;
    const path = `./uploads/${filename}`;
    
    await writeFile(path, file.data);
    
    // Salvar no banco de dados
    const upload = await db.query(
      "INSERT INTO uploads (filename, original_name, size, mime_type, user_id) VALUES ($1, $2, $3, $4, $5) RETURNING *",
      [filename, file.originalName, file.data.length, file.contentType, req.user.userId]
    );
    
    res.status(201).json({
      upload: {
        id: upload.id,
        url: `/uploads/${filename}`,
        originalName: file.originalName,
        size: file.data.length
      }
    });
  }

  @Get("/:filename")
  async getFile(@Param("filename") filename: string, @Res() res: ResponseServer) {
    const upload = await db.query(
      "SELECT * FROM uploads WHERE filename = $1",
      [filename]
    );
    
    if (!upload) {
      throw new HttpError(404, "Arquivo não encontrado");
    }
    
    const file = await readFile(`./uploads/${filename}`);
    
    res.setHeader("Content-Type", upload.mime_type);
    res.setHeader("Content-Length", file.length.toString());
    res.send(file);
  }
}

WebSocket com AzuraJS 🔌

import { AzuraClient } from "azurajs";
import { WebSocketServer } from "ws";

const app = new AzuraClient();
const server = await app.listen(3000);

// Criar WebSocket server
const wss = new WebSocketServer({ server });

interface Client {
  id: string;
  ws: any;
  userId?: string;
}

const clients = new Map<string, Client>();

wss.on("connection", (ws, req) => {
  const clientId = randomUUID();
  
  clients.set(clientId, { id: clientId, ws });
  
  console.log(`Cliente conectado: ${clientId}`);
  
  ws.on("message", (data: Buffer) => {
    try {
      const message = JSON.parse(data.toString());
      
      // Autenticar
      if (message.type === "auth") {
        const token = message.token;
        const decoded = verify(token, JWT_SECRET) as any;
        
        const client = clients.get(clientId);
        if (client) {
          client.userId = decoded.userId;
          ws.send(JSON.stringify({ type: "auth", success: true }));
        }
      }
      
      // Enviar mensagem
      else if (message.type === "message") {
        const client = clients.get(clientId);
        
        if (!client?.userId) {
          return ws.send(JSON.stringify({ 
            type: "error", 
            message: "Não autenticado" 
          }));
        }
        
        // Broadcast para todos os clientes
        const payload = {
          type: "message",
          from: client.userId,
          text: message.text,
          timestamp: new Date().toISOString()
        };
        
        clients.forEach(c => {
          if (c.userId) {
            c.ws.send(JSON.stringify(payload));
          }
        });
      }
    } catch (error) {
      console.error("WebSocket error:", error);
    }
  });
  
  ws.on("close", () => {
    clients.delete(clientId);
    console.log(`Cliente desconectado: ${clientId}`);
  });
});

Integração com Banco de Dados 💾

PostgreSQL com Prisma

// prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        String   @id @default(uuid())
  name      String
  email     String   @unique
  password  String
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        String   @id @default(uuid())
  title     String
  content   String
  published Boolean  @default(false)
  authorId  String
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
// controllers/PostController.ts
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

@Controller("/api/posts")
export class PostController {
  @Get()
  async getPosts(@Query("published") published: string) {
    const posts = await prisma.post.findMany({
      where: published ? { published: published === "true" } : undefined,
      include: {
        author: {
          select: {
            id: true,
            name: true,
            email: true
          }
        }
      },
      orderBy: {
        createdAt: "desc"
      }
    });
    
    return { posts };
  }

  @Post()
  async createPost(@Body() data: any, @Req() req: RequestServer) {
    const post = await prisma.post.create({
      data: {
        title: data.title,
        content: data.content,
        published: data.published || false,
        authorId: req.user.userId
      },
      include: {
        author: {
          select: {
            id: true,
            name: true,
            email: true
          }
        }
      }
    });
    
    return { post };
  }

  @Put("/:id")
  async updatePost(@Param("id") id: string, @Body() data: any) {
    const post = await prisma.post.update({
      where: { id },
      data: {
        title: data.title,
        content: data.content,
        published: data.published
      }
    });
    
    return { post };
  }

  @Delete("/:id")
  async deletePost(@Param("id") id: string, @Res() res: ResponseServer) {
    await prisma.post.delete({
      where: { id }
    });
    
    res.status(204).end();
  }
}

Servidor Completo 🚀

// server.ts
import { AzuraClient, applyDecorators } from "azurajs";
import cluster from "cluster";
import { cpus } from "os";

// Controllers
import { UserController } from "./controllers/UserController";
import { AuthController } from "./controllers/AuthController";
import { PostController } from "./controllers/PostController";
import { UploadController } from "./controllers/UploadController";

// Middleware
import { authMiddleware } from "./middleware/auth";
import { errorHandler } from "./middleware/errorHandler";
import { LoggingMiddleware } from "azurajs/middleware";

const PORT = process.env.PORT || 3000;
const numCPUs = cpus().length;

if (cluster.isPrimary && process.env.NODE_ENV === "production") {
  console.log(`🚀 Starting ${numCPUs} workers...`);
  
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  
  cluster.on("exit", (worker) => {
    console.log(`Worker ${worker.process.pid} died, starting new worker...`);
    cluster.fork();
  });
} else {
  const app = new AzuraClient({
    environment: process.env.NODE_ENV as any || "development",
    server: {
      port: PORT
    },
    logging: {
      level: "info"
    },
    plugins: {
      cors: {
        enabled: true,
        origin: process.env.ALLOWED_ORIGINS?.split(",") || ["http://localhost:3000"],
        credentials: true
      },
      rateLimit: {
        enabled: true,
        windowMs: 60000,
        max: 100
      }
    }
  });
  
  // Middleware global
  app.use(LoggingMiddleware);
  
  // Rotas públicas
  applyDecorators(app, [AuthController]);
  
  // Rotas protegidas
  app.use("/api", authMiddleware);
  applyDecorators(app, [UserController, PostController, UploadController]);
  
  // Error handler (último!)
  app.use(errorHandler);
  
  await app.listen(PORT);
  console.log(`✅ Worker ${process.pid} listening on port ${PORT}`);
}

Testando a API 🧪

// test/api.test.ts
import { describe, it, expect } from "vitest";

const BASE_URL = "http://localhost:3000";

describe("Auth API", () => {
  let token: string;
  
  it("should register a new user", async () => {
    const response = await fetch(`${BASE_URL}/auth/register`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        name: "Test User",
        email: "[email protected]",
        password: "password123"
      })
    });
    
    const data = await response.json();
    expect(response.status).toBe(201);
    expect(data.user).toBeDefined();
    expect(data.token).toBeDefined();
    
    token = data.token;
  });
  
  it("should login", async () => {
    const response = await fetch(`${BASE_URL}/auth/login`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email: "[email protected]",
        password: "password123"
      })
    });
    
    const data = await response.json();
    expect(response.status).toBe(200);
    expect(data.token).toBeDefined();
  });
  
  it("should get current user", async () => {
    const response = await fetch(`${BASE_URL}/auth/me`, {
      headers: {
        "Authorization": `Bearer ${token}`
      }
    });
    
    const data = await response.json();
    expect(response.status).toBe(200);
    expect(data.user.email).toBe("[email protected]");
  });
});

Melhores Práticas ✨

Organize seu código: Use controllers, services, repositories

Valide entrada: Sempre valide dados do cliente

Use TypeScript: Aproveite type safety

Implemente autenticação: Proteja rotas sensíveis

Trate erros: Use error handlers globais

Log tudo: Facilita debugging

Teste sua API: Escreva testes automatizados

Próximos Passos 📖

On this page