🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
MÓDULO 2 - AULA 6

Implementando Rotas Reais

Sistema de Pedidos com Filtros, Paginação e Joins

40 minutos
Avançado
Rota get-orders - Análise Completa

Vamos analisar uma das rotas mais complexas do Restaurantix: a listagem de pedidos com filtros avançados.

Funcionalidades

  • Autenticação: Middleware auth
  • Autorização: Apenas managers
  • Filtros: Status, cliente, ID
  • Paginação: 10 itens por página
  • Joins: Dados do cliente
  • Ordenação: Por status e data

Tecnologias

  • Drizzle ORM: Query builder
  • TypeBox: Validação de query
  • SQL: CASE WHEN para ordenação
  • Joins: Inner join com users
  • Filtros: AND, ILIKE, EQ
  • Paginação: OFFSET/LIMIT
Código Completo - get-orders.ts

Implementação completa

// src/http/routes/get-orders.ts
import { auth } from "../auth";
import { UnauthorizedError } from "../errors/unauthorized-error";
import { createSelectSchema } from "drizzle-typebox";
import { t } from "elysia";
import { eq, and, ilike, count, getTableColumns, desc, sql } from "drizzle-orm";
import { db } from "@/application/infra/db/connection";
import { orders, users } from "@/application/infra/db/schema";

export const getOrders = auth.get(
  "/orders",
  async ({ getCurrentUser, query }) => {
    // 1. AUTENTICAÇÃO E AUTORIZAÇÃO
    const { restaurantId } = await getCurrentUser();
    if (!restaurantId) {
      throw new UnauthorizedError();
    }

    // 2. EXTRAIR PARÂMETROS DE QUERY
    const { customerName, orderId, status, pageIndex } = query;
    
    // 3. PREPARAR COLUNAS DA TABELA ORDERS
    const orderTableColumns = getTableColumns(orders);
    
    // 4. CONSTRUIR QUERY BASE COM JOINS
    const baseQuery = db
      .select({
        ...orderTableColumns,
        customerName: users.name,
      })
      .from(orders)
      .innerJoin(users, eq(users.id, orders.customerId))
      .where(
        and(
          // Filtrar por restaurante do usuário logado
          eq(orders.restaurantId, restaurantId),
          // Filtros opcionais
          orderId ? ilike(orders.id, `%${orderId}%`) : undefined,
          status ? eq(orders.status, status) : undefined,
          customerName ? ilike(users.name, `%${customerName}%`) : undefined,
        ),
      );

    // 5. EXECUTAR QUERIES EM PARALELO
    const [[amountOfOrders], allOrders] = await Promise.all([
      // Contar total de registros
      db.select({ count: count() }).from(baseQuery.as("baseQuery")),
      
      // Buscar registros paginados
      db
        .select()
        .from(baseQuery.as("baseQuery"))
        .offset(pageIndex * 10)
        .limit(10)
        .orderBy((fields) => {
          return [
            // Ordenação customizada por status
            sql`CASE ${fields.status} 
            WHEN 'pending' THEN 1
            WHEN 'preparing' THEN 2
            WHEN 'ready' THEN 3
            WHEN 'delivered' THEN 4
            WHEN 'cancelled' THEN 20
            END`,
            // Depois por data (mais recente primeiro)
            desc(fields.createdAt),
          ];
        }),
    ]);

    // 6. RETORNAR DADOS COM METADADOS
    return {
      orders: allOrders,
      meta: { 
        pageIndex, 
        perPage: 10, 
        totalCount: amountOfOrders?.count ?? 0 
      },
    };
  },
  {
    // 7. VALIDAÇÃO DE QUERY PARAMETERS
    query: t.Object({
      customerName: t.Optional(t.String()),
      orderId: t.Optional(t.String()),
      status: t.Optional(createSelectSchema(orders).properties.status),
      pageIndex: t.Numeric({ minimum: 0 }),
    }),
  },
);
🎯 Exercício Prático

Agora você tem o código real do Restaurantix! Implemente as outras rotas seguindo o mesmo padrão.

✅ Código Real Implementado

Este é o código exato usado no projeto Restaurantix em produção, com todas as funcionalidades reais de filtros, paginação e joins.