Arquitetura Serverless: Como Reduzir Custos e Escalar Infinitamente com JavaScript
Olá HaWkers, serverless deixou de ser tendência futurista para se tornar arquitetura padrão em 2025. Com o mercado SaaS ultrapassando $300 bilhões e gastos em cloud pública atingindo $723.4 bilhões, entender serverless não é mais opcional.
JavaScript, com sua natureza event-driven, é perfeitamente compatível com plataformas como AWS Lambda, Google Cloud Functions e Azure Functions. Mas como aproveitar isso na prática?
O que É Serverless e Por Que Importa
Serverless não significa "sem servidores" - significa que você não gerencia servidores. A infraestrutura é completamente abstraída:
// Modelo Tradicional - Você gerencia tudo
const traditionalModel = {
infrastructure: 'Você provisiona servidores',
scaling: 'Você configura auto-scaling',
availability: 'Você garante uptime',
costs: 'Paga 24/7, mesmo sem tráfego',
maintenance: 'Patches, updates, segurança = sua responsabilidade'
};
// Modelo Serverless - Cloud gerencia tudo
const serverlessModel = {
infrastructure: 'Provisionado automaticamente',
scaling: 'Escala automaticamente (0 a milhões)',
availability: 'SLA de 99.95%+ garantido',
costs: 'Paga APENAS por execuções',
maintenance: 'Gerenciado pelo provider'
};
// Economia real
const costComparison = {
traditional: {
server: 'EC2 t3.medium = ~$30/mês',
usage: 'Rodando 24/7',
traffic: '10.000 requests/mês',
costPerRequest: '$30 / 10.000 = $0.003'
},
serverless: {
lambda: '10.000 requests = $0.20',
freeEcuções '1M execuções/mês free tier',
costPerRequest: '$0.20 / 10.000 = $0.00002',
savings: '99.3% economia vs tradicional'
}
};
AWS Lambda com Node.js: Implementação Prática
Função Lambda Básica
// handler.js - AWS Lambda Function
export const handler = async (event) => {
try {
// Parse do body (se for POST)
const body = event.body ? JSON.parse(event.body) : {};
// Lógica de negócio
const result = await processData(body);
// Resposta HTTP
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
success: true,
data: result
})
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
body: JSON.stringify({
success: false,
error: error.message
})
};
}
};
async function processData(data) {
// Sua lógica aqui
return {
processed: true,
timestamp: new Date().toISOString()
};
}API REST Completa com Lambda
// api/users.js - CRUD de usuários
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
DynamoDBDocumentClient,
GetCommand,
PutCommand,
DeleteCommand,
ScanCommand
} from '@aws-sdk/lib-dynamodb';
const client = new DynamoDBClient({});
const dynamo = DynamoDBDocumentClient.from(client);
const TABLE_NAME = process.env.USERS_TABLE;
export const handler = async (event) => {
const { httpMethod, pathParameters, body } = event;
try {
switch (httpMethod) {
case 'GET':
return await getUser(pathParameters?.id);
case 'POST':
return await createUser(JSON.parse(body));
case 'PUT':
return await updateUser(pathParameters?.id, JSON.parse(body));
case 'DELETE':
return await deleteUser(pathParameters?.id);
default:
return response(405, { error: 'Method not allowed' });
}
} catch (error) {
console.error(error);
return response(500, { error: error.message });
}
};
async function getUser(id) {
if (!id) {
// List all users
const result = await dynamo.send(
new ScanCommand({ TableName: TABLE_NAME })
);
return response(200, result.Items);
}
// Get specific user
const result = await dynamo.send(
new GetCommand({
TableName: TABLE_NAME,
Key: { id }
})
);
return response(200, result.Item);
}
async function createUser(data) {
const user = {
id: crypto.randomUUID(),
...data,
createdAt: new Date().toISOString()
};
await dynamo.send(
new PutCommand({
TableName: TABLE_NAME,
Item: user
})
);
return response(201, user);
}
async function updateUser(id, data) {
const user = {
id,
...data,
updatedAt: new Date().toISOString()
};
await dynamo.send(
new PutCommand({
TableName: TABLE_NAME,
Item: user
})
);
return response(200, user);
}
async function deleteUser(id) {
await dynamo.send(
new DeleteCommand({
TableName: TABLE_NAME,
Key: { id }
})
);
return response(204, {});
}
function response(statusCode, body) {
return {
statusCode,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(body)
};
}
Serverless Framework: Deploy Simplificado
# serverless.yml
service: my-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
environment:
USERS_TABLE: ${self:service}-users-${self:provider.stage}
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.USERS_TABLE}"
functions:
api:
handler: api/users.handler
events:
- httpApi:
path: /users
method: GET
- httpApi:
path: /users/{id}
method: GET
- httpApi:
path: /users
method: POST
- httpApi:
path: /users/{id}
method: PUT
- httpApi:
path: /users/{id}
method: DELETE
resources:
Resources:
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:provider.environment.USERS_TABLE}
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST# Deploy completo com um comando
serverless deploy
# Logs em tempo real
serverless logs -f api --tail
# Remover stack completa
serverless remove
Casos de Uso Perfeitos para Serverless
1. APIs REST/GraphQL
// GraphQL com Apollo Server em Lambda
import { ApolloServer } from '@apollo/server';
import { startServerAndCreateLambdaHandler } from '@as-integrations/aws-lambda';
const typeDefs = `#graphql
type User {
id: ID!
name: String!
email: String!
}
type Query {
users: [User!]!
user(id: ID!): User
}
type Mutation {
createUser(name: String!, email: String!): User!
}
`;
const resolvers = {
Query: {
users: async () => {
// Buscar do DynamoDB
return await getAllUsers();
},
user: async (_, { id }) => {
return await getUserById(id);
}
},
Mutation: {
createUser: async (_, { name, email }) => {
return await createNewUser({ name, email });
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers
});
export const handler = startServerAndCreateLambdaHandler(server);2. Processamento de Arquivos
// Lambda triggered por upload S3
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import sharp from 'sharp';
const s3 = new S3Client({});
export const handler = async (event) => {
// Event de S3 upload
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(event.Records[0].s3.object.key);
try {
// Download da imagem original
const { Body } = await s3.send(
new GetObjectCommand({ Bucket: bucket, Key: key })
);
const imageBuffer = await streamToBuffer(Body);
// Redimensionar para diferentes tamanhos
const sizes = [
{ name: 'thumbnail', width: 150 },
{ name: 'medium', width: 500 },
{ name: 'large', width: 1200 }
];
await Promise.all(
sizes.map(async ({ name, width }) => {
const resized = await sharp(imageBuffer)
.resize(width)
.jpeg({ quality: 80 })
.toBuffer();
const newKey = key.replace(/\.\w+$/, `-${name}.jpg`);
await s3.send(
new PutObjectCommand({
Bucket: bucket,
Key: newKey,
Body: resized,
ContentType: 'image/jpeg'
})
);
})
);
return { statusCode: 200, body: 'Images processed successfully' };
} catch (error) {
console.error(error);
throw error;
}
};
async function streamToBuffer(stream) {
const chunks = [];
for await (const chunk of stream) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
}3. Scheduled Tasks (Cron Jobs)
// Lambda executada diariamente
export const handler = async (event) => {
console.log('Running daily cleanup task...');
try {
// Limpar dados antigos
await cleanupOldData();
// Enviar relatório
await sendDailyReport();
// Backup de dados importantes
await backupCriticalData();
return {
statusCode: 200,
body: JSON.stringify({ message: 'Daily tasks completed' })
};
} catch (error) {
console.error('Daily task error:', error);
await notifyAdmins(error);
throw error;
}
};
async function cleanupOldData() {
// Deletar registros com mais de 90 dias
const ninetyDaysAgo = new Date();
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
// Implementação específica
}
async function sendDailyReport() {
// Gerar e enviar relatório por email
}
async function backupCriticalData() {
// Backup para S3 Glacier
}
async function notifyAdmins(error) {
// SNS ou SES para notificação
}
Vantagens e Desvantagens
✅ Vantagens
- Custo: Pague apenas pelo que usar
- Escalabilidade: Automática e infinita
- Manutenção: Zero overhead operacional
- Deploy: Extremamente rápido
- Segurança: Gerenciada pelo provider
⚠️ Desvantagens
- Cold Start: ~100-500ms na primeira execução
- Tempo Limite: AWS Lambda = 15 min máximo
- Vendor Lock-in: Código acoplado ao provider
- Debugging: Mais complexo que tradicional
- Custos em Alto Volume: Pode ficar caro
Quando Usar Serverless
✅ Use Serverless quando:
- Tráfego variável/imprevisível
- Processamento batch/background
- APIs com baixo/médio tráfego
- Webhooks e integrações
- Protótipos e MVPs
❌ Evite Serverless quando:
- Processamento muito longo (>15 min)
- Tráfego altíssimo e constante
- Necessita state em memória
- Latência crítica (<10ms)
Se você quer entender mais sobre como otimizar código JavaScript para ambientes serverless, leia Performance em JavaScript: Técnicas de Otimização Avançadas onde você vai descobrir como escrever código mais eficiente.
Bora pra cima! 🦅
💻 Domine JavaScript Para o Cloud
Serverless é JavaScript puro executado na cloud. Quanto melhor seu JavaScript, mais você aproveita serverless.
Se você quer construir uma base sólida em JavaScript:
Formas de pagamento:
- R$9,90 (pagamento único)

