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
- 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}`]);
},
},
},
};
- 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()
);
- 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
| Aspecto | REST | GraphQL | gRPC |
|---|---|---|---|
| Formato | JSON | JSON | Protobuf (binário) |
| Performance | Boa | Média | Excelente |
| Caching | Nativo (HTTP) | Complexo | Manual |
| Streaming | Limitado | Subscriptions | Nativo |
| Type Safety | Via OpenAPI | Nativo (schema) | Nativo (proto) |
| Browser Support | ✅ Nativo | ✅ Nativo | ⚠️ grpc-web |
| Learning Curve | Baixa | Média | Média-Alta |
| Tooling | Maduro | Bom | Bom |
Estratégia Híbrida
Muitas organizações usam múltiplos estilos:
Quer definir a estratégia de APIs para sua arquitetura? Fale com nossos especialistas em Software Engineering.