Serverless Architecture em 2025: Por Que Sua Próxima API Deveria Ser Serverless
Olá HaWkers, imagine pagar apenas pelos milissegundos exatos que seu código executa, escalar automaticamente de zero a milhões de requisições, e nunca mais se preocupar com servidores. Bem-vindo à realidade do serverless em 2025.
Você já se perguntou por que empresas como Netflix, Coca-Cola e iRobot migraram partes críticas de suas infraestruturas para serverless? A resposta vai além de custos - é sobre agilidade, escalabilidade e foco no que realmente importa: seu código.
O Que É Serverless e Por Que Agora?
Serverless não significa "sem servidores". Significa que você não gerencia servidores. A infraestrutura é abstraída e gerenciada pelo provedor cloud, e você paga apenas pelo tempo de execução real do seu código.
Em 2025, serverless atingiu maturidade com:
- Cold start reduzido para <50ms
- Suporte nativo a containers
- Edge deployment global
- Integração perfeita com databases e serviços
- Custos até 70% menores que infraestrutura tradicional
Comparação dos Principais Provedores
AWS Lambda: O Gigante Maduro
// AWS Lambda com Node.js
export const handler = async (event) => {
const { userId } = JSON.parse(event.body);
// Conexão com DynamoDB
const dynamodb = new DynamoDB.DocumentClient();
try {
const user = await dynamodb.get({
TableName: 'Users',
Key: { userId }
}).promise();
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(user.Item)
};
} catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: error.message })
};
}
};
// Configuração (serverless.yml)
service: user-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
environment:
DYNAMODB_TABLE: ${self:service}-${opt:stage, self:provider.stage}
functions:
getUser:
handler: handler.handler
events:
- http:
path: users/{userId}
method: get
cors: true
reservedConcurrency: 10 # Limita execuções simultâneas
Vantagens AWS Lambda:
- Ecossistema AWS completo
- 15 minutos de timeout (mais longo)
- Suporte a containers
- Preço: $0.20 por 1M requisições + compute
Vercel Functions: Simplicidade para Frontend
// api/user/[id].ts (Vercel Functions)
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
export const config = {
runtime: 'edge', // ou 'nodejs'
};
export default async function handler(req: NextRequest) {
const { searchParams } = new URL(req.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json(
{ error: 'User ID required' },
{ status: 400 }
);
}
try {
const user = await prisma.user.findUnique({
where: { id },
select: {
id: true,
name: true,
email: true,
createdAt: true
}
});
if (!user) {
return NextResponse.json(
{ error: 'User not found' },
{ status: 404 }
);
}
return NextResponse.json(user);
} catch (error) {
console.error('Database error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
Vantagens Vercel:
- Deploy automático com Git push
- Edge Functions (latência ultra-baixa)
- Integração perfeita com Next.js
- Preço: Free tier generoso, $20/mês Pro
Cloudflare Workers: O Mais Rápido
// Cloudflare Worker
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
const userId = url.pathname.split('/').pop();
// KV é o armazenamento key-value do Cloudflare
const user = await USER_KV.get(userId, 'json');
if (!user) {
return new Response(
JSON.stringify({ error: 'User not found' }),
{
status: 404,
headers: { 'Content-Type': 'application/json' }
}
);
}
// Executa em edge locations globais
return new Response(JSON.stringify(user), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=60'
}
});
}
// Middleware para autenticação
async function authenticate(request) {
const token = request.headers.get('Authorization');
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
// Valida JWT no edge
const isValid = await verifyJWT(token);
if (!isValid) {
return new Response('Invalid token', { status: 401 });
}
return null; // Continue
}
Vantagens Cloudflare:
- Latência global <10ms
- Cold start praticamente zero
- 200+ edge locations
- Preço: $5/mês (10M requisições)
Casos de Uso Perfeitos para Serverless
1. APIs REST e GraphQL
// API GraphQL serverless com Apollo
import { ApolloServer } from '@apollo/server';
import { startServerAndCreateLambdaHandler } from '@as-integrations/aws-lambda';
const typeDefs = `
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
type Query {
user(id: ID!): User
users: [User!]!
post(id: ID!): Post
}
type Mutation {
createPost(title: String!, content: String!, authorId: ID!): Post!
updatePost(id: ID!, title: String, content: String): Post!
}
`;
const resolvers = {
Query: {
user: async (_, { id }, context) => {
return await context.dataSources.userAPI.getUser(id);
},
users: async (_, __, context) => {
return await context.dataSources.userAPI.getUsers();
},
post: async (_, { id }, context) => {
return await context.dataSources.postAPI.getPost(id);
}
},
Mutation: {
createPost: async (_, args, context) => {
return await context.dataSources.postAPI.createPost(args);
}
},
User: {
posts: async (user, _, context) => {
return await context.dataSources.postAPI.getPostsByAuthor(user.id);
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers
});
export const handler = startServerAndCreateLambdaHandler(server);
2. Processamento de Dados em Lote
// Lambda para processamento assíncrono com SQS
export const handler = async (event) => {
// Recebe mensagens do SQS
const records = event.Records;
const results = await Promise.all(
records.map(async (record) => {
const message = JSON.parse(record.body);
try {
// Processa imagem, gera thumbnail, etc
await processImage(message.imageUrl);
// Remove mensagem da fila
return {
itemIdentifier: record.messageId,
batchItemFailures: []
};
} catch (error) {
console.error('Processing failed:', error);
// Mensagem volta para fila
return {
batchItemFailures: [{ itemIdentifier: record.messageId }]
};
}
})
);
return { batchItemFailures: [] };
};
async function processImage(imageUrl) {
// Download da imagem
const response = await fetch(imageUrl);
const buffer = await response.arrayBuffer();
// Redimensiona usando sharp
const thumbnail = await sharp(buffer)
.resize(200, 200, { fit: 'cover' })
.toBuffer();
// Upload para S3
await s3.putObject({
Bucket: 'thumbnails',
Key: `thumb_${Date.now()}.jpg`,
Body: thumbnail,
ContentType: 'image/jpeg'
});
}
3. Webhooks e Integrações
// Webhook do Stripe em Vercel Function
import { NextRequest } from 'next/server';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
export const config = {
api: {
bodyParser: false, // Stripe precisa do raw body
},
};
export default async function handler(req: NextRequest) {
if (req.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const body = await req.text();
const sig = req.headers.get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
} catch (err) {
console.error('Webhook signature verification failed:', err);
return new Response('Webhook error', { status: 400 });
}
// Handle eventos do Stripe
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
await fulfillOrder(paymentIntent.id);
break;
case 'customer.subscription.created':
const subscription = event.data.object;
await activateSubscription(subscription.customer as string);
break;
case 'invoice.payment_failed':
const invoice = event.data.object;
await notifyPaymentFailure(invoice.customer as string);
break;
}
return new Response(JSON.stringify({ received: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
4. Scheduled Jobs (Cron)
// Lambda com EventBridge (Cron)
// Executa todo dia às 2am para limpar dados antigos
export const handler = async () => {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
// Limpa registros antigos
const result = await dynamodb.scan({
TableName: 'Logs',
FilterExpression: 'createdAt < :date',
ExpressionAttributeValues: {
':date': thirtyDaysAgo.toISOString()
}
}).promise();
// Delete em batch
const deletePromises = result.Items.map(item =>
dynamodb.delete({
TableName: 'Logs',
Key: { id: item.id }
}).promise()
);
await Promise.all(deletePromises);
console.log(`Deleted ${deletePromises.length} old records`);
// Envia métrica para CloudWatch
await cloudwatch.putMetricData({
Namespace: 'Cleanup',
MetricData: [{
MetricName: 'RecordsDeleted',
Value: deletePromises.length,
Unit: 'Count'
}]
}).promise();
return {
statusCode: 200,
body: JSON.stringify({ deleted: deletePromises.length })
};
};
Boas Práticas Serverless
1. Gerenciamento de Conexões de Database
// ❌ Ruim: Cria nova conexão a cada invocação
export const handler = async (event) => {
const db = await mongoose.connect(process.env.MONGO_URL);
// ... usa db ...
await db.disconnect();
};
// ✅ Bom: Reutiliza conexão entre invocações
let cachedDb = null;
async function connectToDatabase() {
if (cachedDb) {
return cachedDb;
}
cachedDb = await mongoose.connect(process.env.MONGO_URL, {
bufferCommands: false,
maxPoolSize: 1 // Lambda reusa poucas conexões
});
return cachedDb;
}
export const handler = async (event) => {
const db = await connectToDatabase();
// ... usa db ...
// NÃO desconecta - deixa para próxima invocação
};
2. Otimização de Cold Start
// Importações no top-level são executadas uma vez
import { DynamoDB } from 'aws-sdk';
import { someHeavyLibrary } from 'heavy-lib';
// Inicialização fora do handler
const dynamodb = new DynamoDB.DocumentClient();
// Handler minimalista
export const handler = async (event) => {
// Código leve e focado
const result = await dynamodb.get({
TableName: 'Data',
Key: { id: event.id }
}).promise();
return result.Item;
};
3. Error Handling e Retry
// Implementa retry com exponential backoff
async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
export const handler = async (event) => {
try {
const result = await withRetry(async () => {
return await externalAPI.call();
});
return { statusCode: 200, body: JSON.stringify(result) };
} catch (error) {
// Log estruturado para CloudWatch
console.error(JSON.stringify({
error: error.message,
stack: error.stack,
event: event
}));
return {
statusCode: 500,
body: JSON.stringify({ error: 'Internal server error' })
};
}
};
Custos: Serverless vs Tradicional
Exemplo Real: API com 1M req/mês
Serverless (AWS Lambda):
- 1M requisições: $0.20
- Compute (100ms avg): $1.67
- Total: ~$2/mês
Tradicional (EC2 t3.small):
- Instância 24/7: $15.18/mês
- Load balancer: $16.20/mês
- Total: ~$31/mês
Economia: 93%
Quando Serverless Não é Ideal
- Aplicações com tráfego constante 24/7 - Servidor dedicado pode ser mais barato
- Processamento de longa duração - Lambda tem limite de 15min
- Aplicações stateful complexas - WebSocket de longa duração
- Requisitos de GPU - Ainda limitado em serverless
O Futuro do Serverless
Em 2025, estamos vendo:
- WASM no edge para performance nativa
- Stateful serverless com Durable Functions
- Multi-cloud simplificado
- AI-powered optimization automática
Se você está interessado em outras arquiteturas modernas, recomendo que dê uma olhada em outro artigo: Server-First Development: O Futuro com Astro, Remix e SvelteKit onde você vai descobrir como frameworks modernos estão repensando arquitetura web.
Bora pra cima! 🦅
💻 Domine JavaScript de Verdade
O conhecimento que você adquiriu neste artigo é só o começo. Há técnicas, padrões e práticas que transformam desenvolvedores iniciantes em profissionais requisitados.
Invista no Seu Futuro
Preparei um material completo para você dominar JavaScript:
Formas de pagamento:
- 3x de R$34,54 sem juros
- ou R$97,90 à vista