Pular para o conteúdo
TypeScript

Tipar Middleware no Next.js com TypeScript

Domine a tipagem de middleware no Next.js. De NextRequest e NextResponse até matcher config e middleware condicional, tudo com exemplos do dia a dia.

Por que isso é importante

Tipar Middleware no Next.js com TypeScript. Domine a tipagem de middleware no Next.js. De NextRequest e NextResponse até matcher config e middleware condicional, tudo com exemplos do dia a dia.

O Que É Middleware no Next.js e Como TypeScript Entra

Middleware no Next.js é uma função que intercepta requests antes de chegar na página. Pensa nele como um porteiro: verifica credenciais, redireciona quem não deve entrar e adiciona informações no request.

O Next.js exporta os tipos NextRequest e NextResponse diretamente do pacote next/server. O tipo da função middleware em si é NextMiddleware. Quando você usa esses tipos, o editor te dá autocomplete de tudo: cookies, headers, geo, ip, nextUrl e muito mais.

Sem tipagem, você trabalha no escuro. Com tipagem, cada propriedade do request aparece no autocomplete e cada retorno errado gera erro de compilação. É a diferença entre adivinhar e ter certeza.

O middleware roda no Edge Runtime, que tem APIs diferentes do Node.js. Os tipos do Next.js já refletem isso. Então se você tentar usar algo que não existe no Edge, o TypeScript avisa na hora.

Como Tipar Middleware Passo a Passo

Vamos montar um middleware tipado do zero. Cada passo adiciona mais segurança.

  1. Passo 1 - Importe os tipos certos: Use import { NextRequest, NextResponse } from 'next/server'. Esses dois tipos são a base de todo middleware tipado no Next.js.
  2. Passo 2 - Tipando a função middleware: Declare export function middleware(request: NextRequest): NextResponse | Response. O retorno pode ser NextResponse, Response ou undefined (quando quer seguir sem alteração).
  3. Passo 3 - Configure o matcher: Exporte export const config = { matcher: ['/dashboard/:path*', '/api/:path*'] }. O matcher define quais rotas passam pelo middleware. Tipar como { matcher: string | string[] } garante que você não coloque valor errado.
  4. Passo 4 - Acesse propriedades tipadas: request.nextUrl, request.cookies, request.headers e request.geo já vêm com tipos completos. Use sem medo.
  5. Passo 5 - Retorne respostas tipadas: Use NextResponse.redirect(), NextResponse.rewrite() ou NextResponse.next(). Cada método tem tipagem própria e o editor te guia.
  6. Passo 6 - Middleware condicional: Crie lógica condicional tipada baseada na URL, cookies ou headers. O TypeScript garante que cada branch retorna o tipo correto.

Exemplos Práticos de Middleware Tipado

Vamos ver código que você usa em projetos reais. Do básico ao avançado.

Middleware Básico com NextRequest e NextResponse

// middleware.ts (na raiz do projeto)
import { NextRequest, NextResponse } from 'next/server';

export function middleware(request: NextRequest): NextResponse {
  // request.nextUrl é tipado como NextURL
  const { pathname } = request.nextUrl;

  // request.cookies é tipado como RequestCookies
  const token = request.cookies.get('auth-token')?.value;

  if (pathname.startsWith('/dashboard') && !token) {
    // NextResponse.redirect espera uma URL
    const loginUrl = new URL('/login', request.url);
    return NextResponse.redirect(loginUrl);
  }

  return NextResponse.next();
}

// Matcher tipado: define onde o middleware roda
export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*'],
};

Middleware com Headers e Cookies Tipados

import { NextRequest, NextResponse } from 'next/server';

// Tipo customizado pra dados do token
interface TokenPayload {
  userId: string;
  role: 'admin' | 'user' | 'editor';
  exp: number;
}

function decodeToken(token: string): TokenPayload | null {
  try {
    // Decodificação simplificada pra exemplo
    const payload = JSON.parse(atob(token.split('.')[1]));
    return payload as TokenPayload;
  } catch {
    return null;
  }
}

export function middleware(request: NextRequest): NextResponse {
  const token = request.cookies.get('session')?.value;
  const response = NextResponse.next();

  if (token) {
    const payload = decodeToken(token);

    if (payload) {
      // Adicionar headers tipados na response
      response.headers.set('x-user-id', payload.userId);
      response.headers.set('x-user-role', payload.role);
    }
  }

  // Setar cookie tipado
  response.cookies.set('visited', 'true', {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 60 * 60 * 24, // 24 horas
  });

  return response;
}

Middleware Condicional com Múltiplas Rotas

import { NextRequest, NextResponse } from 'next/server';

// Tipo pra regras de middleware
type MiddlewareRule = {
  pattern: RegExp;
  handler: (req: NextRequest) => NextResponse | null;
};

const rules: MiddlewareRule[] = [
  {
    pattern: /^\/api\//,
    handler: (req: NextRequest): NextResponse | null => {
      // Rate limiting pra APIs
      const ip = req.headers.get('x-forwarded-for') ?? 'unknown';
      const response = NextResponse.next();
      response.headers.set('x-rate-limit-ip', ip);
      return response;
    },
  },
  {
    pattern: /^\/admin/,
    handler: (req: NextRequest): NextResponse | null => {
      const role = req.cookies.get('role')?.value;
      if (role !== 'admin') {
        return NextResponse.redirect(new URL('/unauthorized', req.url));
      }
      return null; // Continua pro proximo handler
    },
  },
];

export function middleware(request: NextRequest): NextResponse {
  for (const rule of rules) {
    if (rule.pattern.test(request.nextUrl.pathname)) {
      const result = rule.handler(request);
      if (result) return result;
    }
  }
  return NextResponse.next();
}

export const config = {
  matcher: ['/api/:path*', '/admin/:path*', '/dashboard/:path*'],
};

Tipando o Matcher Config

// O Next.js espera essa estrutura pro config do middleware
// Você pode tipar explicitamente pra documentar

import type { NextConfig } from 'next';

// Matcher aceita string, array de strings ou objetos
export const config = {
  matcher: [
    // Rota simples
    '/about',
    // Com path params
    '/blog/:slug',
    // Com wildcard
    '/dashboard/:path*',
    // Negando rotas (não aplicar middleware)
    '/((?!_next/static|_next/image|favicon.ico).*)',
  ],
};

// Também dá pra usar objeto com source e condições
export const configAvancado = {
  matcher: [
    {
      source: '/api/:path*',
      has: [
        { type: 'header' as const, key: 'authorization' },
      ],
    },
  ],
};

Cada exemplo mostra como o TypeScript elimina adivinhação. O editor sabe exatamente quais métodos estão disponíveis em NextRequest e NextResponse, e te avisa quando algo não encaixa.

Erros Comuns ao Tipar Middleware

Armadilhas que pegam até dev experiente

Importar do pacote errado: NextRequest e NextResponse vêm de 'next/server', não de 'next'. Se importar errado, os tipos não batem e nada funciona.

Esquecer que middleware roda no Edge Runtime: não dá pra usar APIs do Node.js como fs, path ou Buffer diretamente. O TypeScript avisa se os tipos do Edge não incluem o que você tá tentando usar.

Retornar void quando deveria retornar NextResponse: se o middleware não retorna nada, o Next.js segue sem alteração. Mas se você tipar o retorno como NextResponse sem incluir undefined, o compilador reclama.

Não tipar o payload decodificado: ao ler cookies ou headers com dados JSON, faça parse e valide com type guard antes de usar. Confiar em 'as Tipo' sem validação é receita pra bug silencioso.

Matcher muito amplo: usar '/:path*' faz o middleware rodar em TODA request, incluindo assets estáticos. Isso mata a performance. Sempre filtre com o matcher adequado.

Checklist de Middleware Tipado no Next.js

  • Importou NextRequest e NextResponse de 'next/server'
  • Função middleware tem tipo de retorno explícito
  • Matcher config filtra só as rotas necessárias
  • Cookies e headers são acessados com métodos tipados
  • Payloads decodificados são validados com type guard
  • Redirecionamentos usam NextResponse.redirect com URL tipada
  • Edge Runtime compatível: sem APIs exclusivas do Node.js
  • Middleware condicional retorna NextResponse em todos os branches

TypeScript Profissional na Prática

Tipar middleware é uma das habilidades que separa quem monta projeto sério de quem fica no hello world. No CrazyStack, você constrói um projeto completo com Next.js, TypeScript e Node.js. Middleware de autenticação, proteção de rotas, rate limiting, tudo tipado do zero ao deploy.

Se você quer dominar TypeScript em projetos reais e parar de chutar tipos, esse é o caminho mais direto.

Perguntas frequentes

O Que É Middleware no Next.js e Como TypeScript Entra

Middleware no Next.js é uma função que intercepta requests antes de chegar na página. Pensa nele como um porteiro: verifica credenciais, redireciona quem não deve entrar e adiciona informações no request. O Next.js exporta os tipos NextRequest e NextResponse diretamente do pacote next/server. O tipo da função middleware em si é NextMiddleware. Quando você usa esses tipos, o editor te dá autocomplete de tudo: cookies, headers, geo, ip, nextUrl e muito mais. Sem tipagem, você trabalha no escuro. Com tipagem, cada propriedade do request aparece no autocomplete e cada retorno errado gera erro de compilação. É a diferença entre adivinhar e ter certeza.

Como Tipar Middleware Passo a Passo

Vamos montar um middleware tipado do zero. Cada passo adiciona mais segurança.