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.
import { NextRequest, NextResponse } from 'next/server'. Esses dois tipos são a base de todo middleware tipado no Next.js.export function middleware(request: NextRequest): NextResponse | Response. O retorno pode ser NextResponse, Response ou undefined (quando quer seguir sem alteração).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.request.nextUrl, request.cookies, request.headers e request.geo já vêm com tipos completos. Use sem medo.NextResponse.redirect(), NextResponse.rewrite() ou NextResponse.next(). Cada método tem tipagem própria e o editor te guia.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
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.
Continue lendo
Como Tipar Layout e Template no Next.js com TypeScript
A tipar layouts, templates e parallel routes no Next.js com TypeScript.
Como Tipar Page Params no Next.js com TypeScript
Tipagem correta de params e searchParams em pages do Next.js.
Como Criar Type Guard no TypeScript
A criar type guards pra validar tipos em runtime no TypeScript.
Interface no TypeScript
Quando usar interface