Voltar para o Blog
Anúncio

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
Anúncio

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

serverless deployment

Anúncio

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);
Anúncio

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' }
  });
}
Anúncio

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' })
    };
  }
};
Anúncio

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

  1. Aplicações com tráfego constante 24/7 - Servidor dedicado pode ser mais barato
  2. Processamento de longa duração - Lambda tem limite de 15min
  3. Aplicações stateful complexas - WebSocket de longa duração
  4. 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

📖 Ver Conteúdo Completo

Anúncio
Post anteriorPróximo post

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário