Pular para o conteúdo
Software Engineering 12 min read

API Design: REST vs GraphQL vs gRPC - Quando Usar Cada Um

Compare REST, GraphQL e gRPC para design de APIs, entenda os trade-offs de cada abordagem e aprenda a escolher a melhor opção para seu caso de uso.

Por Equipe Integr8 24/12/2024

O Cenário Atual de APIs

APIs são o backbone de sistemas modernos. A escolha do estilo de API impacta performance, developer experience, e manutenibilidade. Não existe “melhor” opção - existe a opção certa para cada contexto.

🌐

REST

Padrão da indústria, stateless, recursos e verbos HTTP

📊

GraphQL

Query language flexível, busque exatamente o que precisa

gRPC

Protocol Buffers, alta performance, streaming bidirecional

REST: O Clássico

REST (Representational State Transfer) é o estilo mais adotado para APIs web.

Princípios REST

    API REST Bem Desenhada

    # OpenAPI Specification
    openapi: 3.0.0
    info:
      title: Orders API
      version: 1.0.0
    
    paths:
      /orders:
        get:
          summary: List orders
          parameters:
            - name: status
              in: query
              schema:
                type: string
                enum: [pending, paid, shipped]
            - name: page
              in: query
              schema:
                type: integer
                default: 1
            - name: limit
              in: query
              schema:
                type: integer
                default: 20
          responses:
            '200':
              description: Success
              content:
                application/json:
                  schema:
                    type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Order'
                      pagination:
                        $ref: '#/components/schemas/Pagination'
                      _links:
                        type: object
                        properties:
                          self:
                            type: string
                          next:
                            type: string
    
        post:
          summary: Create order
          requestBody:
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/CreateOrderRequest'
          responses:
            '201':
              description: Created
              headers:
                Location:
                  schema:
                    type: string
                  description: URL do novo recurso
    
      /orders/{orderId}:
        get:
          summary: Get order by ID
          parameters:
            - name: orderId
              in: path
              required: true
              schema:
                type: string
          responses:
            '200':
              description: Success
            '404':
              description: Not found

    Versionamento de API REST

    // URL versioning (mais comum)
    // GET /v1/orders
    // GET /v2/orders
    
    // Header versioning
    // GET /orders
    // Accept: application/vnd.myapi.v2+json
    
    // Query param versioning
    // GET /orders?version=2
    💡Quando usar REST
    • APIs públicas que precisam ser fáceis de entender
    • CRUD simples sem necessidades especiais
    • Caching é importante (HTTP caching nativo)
    • Equipe já domina HTTP e REST

    GraphQL: Flexibilidade Total

    GraphQL permite que o cliente especifique exatamente quais dados precisa.

    Schema Definition

    # schema.graphql
    type Query {
      order(id: ID!): Order
      orders(filter: OrderFilter, pagination: PaginationInput): OrderConnection!
      user(id: ID!): User
    }
    
    type Mutation {
      createOrder(input: CreateOrderInput!): CreateOrderPayload!
      updateOrderStatus(orderId: ID!, status: OrderStatus!): Order!
      cancelOrder(orderId: ID!): Order!
    }
    
    type Subscription {
      orderStatusChanged(orderId: ID!): Order!
    }
    
    type Order {
      id: ID!
      status: OrderStatus!
      customer: User!
      items: [OrderItem!]!
      totalAmount: Float!
      createdAt: DateTime!
      shippingAddress: Address
    }
    
    type OrderItem {
      id: ID!
      product: Product!
      quantity: Int!
      unitPrice: Float!
    }
    
    type User {
      id: ID!
      name: String!
      email: String!
      orders(first: Int, after: String): OrderConnection!
    }
    
    input CreateOrderInput {
      customerId: ID!
      items: [OrderItemInput!]!
      shippingAddressId: ID!
    }
    
    enum OrderStatus {
      PENDING
      PAID
      SHIPPED
      DELIVERED
      CANCELLED
    }

    Queries Flexíveis

    # O cliente pede exatamente o que precisa
    
    # Query simples
    query GetOrderSummary($orderId: ID!) {
      order(id: $orderId) {
        id
        status
        totalAmount
      }
    }
    
    # Query com relacionamentos
    query GetOrderDetails($orderId: ID!) {
      order(id: $orderId) {
        id
        status
        totalAmount
        createdAt
        customer {
          name
          email
        }
        items {
          quantity
          unitPrice
          product {
            name
            imageUrl
          }
        }
        shippingAddress {
          street
          city
          country
        }
      }
    }
    
    # Múltiplas queries em uma request
    query Dashboard {
      recentOrders: orders(filter: { status: PENDING }, pagination: { first: 5 }) {
        edges {
          node {
            id
            totalAmount
          }
        }
      }
      topCustomers: users(orderBy: TOTAL_SPENT_DESC, first: 10) {
        name
        totalSpent
      }
    }

    Resolvers

    // resolvers.ts
    const resolvers = {
      Query: {
        order: async (_, { id }, { dataSources }) => {
          return dataSources.ordersAPI.getOrder(id);
        },
        orders: async (_, { filter, pagination }, { dataSources }) => {
          return dataSources.ordersAPI.getOrders(filter, pagination);
        },
      },
    
      Mutation: {
        createOrder: async (_, { input }, { dataSources, user }) => {
          const order = await dataSources.ordersAPI.createOrder(input);
          return { order, success: true };
        },
      },
    
      Order: {
        // Field resolver - busca customer apenas se solicitado
        customer: async (order, _, { dataSources }) => {
          return dataSources.usersAPI.getUser(order.customerId);
        },
        items: async (order, _, { dataSources }) => {
          return dataSources.ordersAPI.getOrderItems(order.id);
        },
      },
    
      Subscription: {
        orderStatusChanged: {
          subscribe: (_, { orderId }, { pubsub }) => {
            return pubsub.asyncIterator([`ORDER_STATUS_${orderId}`]);
          },
        },
      },
    };
    ⚠️Cuidados com GraphQL
    • N+1 queries: Use DataLoader para batching
    • Queries complexas: Implemente query complexity analysis
    • Caching: Mais difícil que REST (use persisted queries)
    • File uploads: Não é nativo, precisa de extensões

    gRPC: Alta Performance

    gRPC usa Protocol Buffers para serialização eficiente e suporta streaming.

    Protocol Buffers

    // orders.proto
    syntax = "proto3";
    
    package orders;
    
    service OrderService {
      // Unary RPC
      rpc GetOrder(GetOrderRequest) returns (Order);
      rpc CreateOrder(CreateOrderRequest) returns (Order);
    
      // Server streaming
      rpc ListOrders(ListOrdersRequest) returns (stream Order);
    
      // Client streaming
      rpc BatchCreateOrders(stream CreateOrderRequest) returns (BatchCreateResponse);
    
      // Bidirectional streaming
      rpc OrderUpdates(stream OrderUpdateRequest) returns (stream Order);
    }
    
    message Order {
      string id = 1;
      string customer_id = 2;
      OrderStatus status = 3;
      repeated OrderItem items = 4;
      double total_amount = 5;
      google.protobuf.Timestamp created_at = 6;
    }
    
    message OrderItem {
      string product_id = 1;
      int32 quantity = 2;
      double unit_price = 3;
    }
    
    enum OrderStatus {
      ORDER_STATUS_UNSPECIFIED = 0;
      ORDER_STATUS_PENDING = 1;
      ORDER_STATUS_PAID = 2;
      ORDER_STATUS_SHIPPED = 3;
      ORDER_STATUS_DELIVERED = 4;
    }
    
    message GetOrderRequest {
      string order_id = 1;
    }
    
    message ListOrdersRequest {
      string customer_id = 1;
      OrderStatus status = 2;
      int32 page_size = 3;
      string page_token = 4;
    }
    
    message CreateOrderRequest {
      string customer_id = 1;
      repeated OrderItem items = 2;
      string shipping_address_id = 3;
    }

    Server Implementation (Node.js)

    // server.ts
    import * as grpc from '@grpc/grpc-js';
    import { OrderServiceService } from './generated/orders_grpc_pb';
    
    const orderService: grpc.UntypedServiceImplementation = {
      getOrder: async (call, callback) => {
        const orderId = call.request.getOrderId();
        const order = await orderRepository.findById(orderId);
    
        if (!order) {
          callback({
            code: grpc.status.NOT_FOUND,
            message: `Order ${orderId} not found`,
          });
          return;
        }
    
        callback(null, orderToProto(order));
      },
    
      listOrders: async (call) => {
        const customerId = call.request.getCustomerId();
        const orders = await orderRepository.findByCustomer(customerId);
    
        for (const order of orders) {
          call.write(orderToProto(order));
        }
        call.end();
      },
    
      orderUpdates: (call) => {
        call.on('data', async (request) => {
          const order = await orderRepository.update(
            request.getOrderId(),
            request.getStatus()
          );
          call.write(orderToProto(order));
        });
    
        call.on('end', () => {
          call.end();
        });
      },
    };
    
    const server = new grpc.Server();
    server.addService(OrderServiceService, orderService);
    server.bindAsync(
      '0.0.0.0:50051',
      grpc.ServerCredentials.createInsecure(),
      () => server.start()
    );
    Quando usar gRPC
    • Comunicação entre microserviços (service-to-service)
    • Quando performance é crítica
    • Streaming de dados (real-time updates)
    • Polyglot environments (suporte a muitas linguagens)

    Comparativo

    AspectoRESTGraphQLgRPC
    FormatoJSONJSONProtobuf (binário)
    PerformanceBoaMédiaExcelente
    CachingNativo (HTTP)ComplexoManual
    StreamingLimitadoSubscriptionsNativo
    Type SafetyVia OpenAPINativo (schema)Nativo (proto)
    Browser Support✅ Nativo✅ Nativo⚠️ grpc-web
    Learning CurveBaixaMédiaMédia-Alta
    ToolingMaduroBomBom

    Estratégia Híbrida

    Muitas organizações usam múltiplos estilos:

    GraphQL/REST para frontend, gRPC entre microserviços
    100%
    Estratégia Híbrida de APIs

    GraphQL/REST para frontend, gRPC entre microserviços


      Quer definir a estratégia de APIs para sua arquitetura? Fale com nossos especialistas em Software Engineering.