Pular para o conteúdo

Middleware

Middleware lhe permite interceptar requisições e respostas e injetar comportamentos dinamicamente toda vez que uma página ou endpoint for ser renderizado.

Isso também permite definir e compartilhar informações específicas da requisição entre endpoints e páginas alterando o objeto locals que é disponibilizado em todos os componentes Astro e endpoints de API.

Middleware está disponível tanto para projetos Astro SSG quanto SSR.

Uso Básico

  1. Crie um arquivo src/middleware.js|ts (Como alternativa, você pode criar src/middleware/index.js|ts.)

  2. Dentro deste arquivo, exporte uma função onRequest(). Esta exportação não pode ser um default export.

    src/middleware.js
    export function onRequest ({ locals, request }, next) {
    // intercepta dados de uma requisição
    // opcionalmente, transforma a requisição modificando `locals`
    locals.titulo = "Novo Título";
    // retorna uma `Response` ou o resultado de chamar `next()`
    return next();
    };
  3. Dentro de qualquer arquivo .astro, acesse os dados da resposta utilizando Astro.locals.

src/components/Componente.astro
---
const dados = Astro.locals;
---
<h1>{dados.titulo}</h1>
<p>Essa {dados.propriedade} veio do middleware.</p>

Tipos do Middleware

Você pode importar e utilizar a função utilitária defineMiddleware() para se aproveitar da segurança de tipo:

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
// `context` e `next` são tipados automaticamente
export const onRequest = defineMiddleware((context, next) => {
});

De outra forma, se você estiver utilizando JsDoc para se aproveitar da segurança de tipo, você pode utilizar MiddlewareRequestHandler:

src/middleware.js
/**
* @type {import("astro").MiddlewareResponseHandler}
*/
// `context` e `next` são tipados automaticamente
export const onRequest = (context, next) => {
};

Para tipar a informação dentro de Astro.locals, o que lhe fornece preenchimento automático dentro de arquivos .astro e do código do middleware, declare um namespace global no arquivo env.d.ts:

src/env.d.ts
/// <reference types="astro/client" />
declare namespace App {
interface Locals {
usuario: {
nome: string
},
tituloBoasVindas: () => string,
compras: Map<string, object>
}
}

Então, dentro do arquivo do middleware, podemos nos aproveitar de preenchimento automático e segurança de tipos.

Você pode armazenar dados de qualquer tipo dentro de Astro.locals: strings, numbers, e até mesmo tipos complexos como funções e maps.

src/middleware.js
export function onRequest ({ locals, request }, next) {
// intercepta dados de resposta de uma requisição
// opcionalmente, transforma a resposta modificando `locals`
locals.usuario.nome = "John Wick";
locals.tituloBoasVindas = () => {
return "Bem-vindo de volta, " + locals.usuario.nome;
};
// retorna uma `Response` ou o resultado de chamar `next()`
return next();
};

Desta forma você pode utilizar essas informações dentro de qualquer arquivo .astro.

src/pages/compras.astro
---
const titulo = Astro.locals.tituloBoasVindas();
const compras = Array.from(Astro.locals.compras.entries());
---
<h1>{titulo}</h1>
<p>Essa {data.propriedade} veio do middleware.</p>
<ul>
{compras.map(compra => {
return <li>{/* faça algo com cada compra */}</li>;
})}
</ul>

Exemplo: editando informações sensíveis

O exemplo abaixo utiliza middleware para substituir “INFORMAÇÃO PESSOAL” pela palavra “REMOVIDO” para lhe permitir renderizar HTML modificado na sua página:

src/middleware.js
export const onRequest = async (context, next) => {
const resposta = await next();
const html = await response.text();
const htmlDaResposta = html.replaceAll("INFORMAÇÃO PESSOAL", "REMOVIDO");
return new Response(redactedHtml, {
status: 200,
headers: response.headers
});
};

Encadeando middleware

Múltiplos middlewares podem ser conectados em uma sequência ordenada utilizando sequence():

src/middleware.js
import { sequence } from "astro:middleware";
async function validacao(_, next) {
console.log("validação da requisição");
const resposta = await next();
console.log("validação da resposta");
return resposta;
}
async function auth(_, next) {
console.log("autenticação da requisição");
const resposta = await next();
console.log("autenticação da resposta");
return resposta;
}
async function cumprimentos(_, next) {
console.log("cumprimentos da requisição");
const resposta = await next();
console.log("comprimentos da resposta");
return resposta;
}
export const onRequest = sequence(validacao, auth, cumprimentos);

O exemplo acima resultará na seguinte ordem no terminal:

Terminal window
validação da requisição
autenticação da requisição
cumprimentos da requisição
cumprimentos da resposta
autenticação da resposta
validação da resposta

Referência da API

onRequest()

Uma função exportada obrigatória do arquivo src/middleware.js que será chamada antes da renderização de cada página ou rota da API. Ela aceita dois argumentos opcionais: context e next(). onRequest() deve retornar uma Response: ou diretamente, ou chamando next().

context

Um objeto que inclui informações a serem disponibilizadas para outro middleware, rotas de API e rotas .astro durante o processo de renderização.

Esse é um argumento opcional passado para onRequest() que pode conter o objeto locals junto com qualquer propriedade adicional a ser compartilhada durante a renderização. Por exemplo, o objeto context pode incluir cookies utilizados para autenticação.

Esse é o mesmo objeto context que é passada para as rotas de API.

next()

Uma função que intercepta (lê e modifica) a resposta (Response) de uma requisição (Request) ou invoca o próximo middleware na sequência e retorna uma Response. Por exemplo, essa função pode modificar o corpo HTML de uma resposta.

Esse é um argumento opcional de onRequest(), e pode prover a Response que o middleware deve retornar.

locals

Um objeto contendo dados de uma Response que podem ser manipulados dentro do middleware.

Esse objeto locals é repassado através do processo de manipulação da requisição e é diponibilizado como uma propriedade em APIContext e AstroGlobal. Isso permite que dados sejam compartilados entre middlewares, rotas da API, e páginas .astro. Isso é útil para armazenar dados específicos de uma chamada, como dados do usuário, através das etapas de renderização.

locals é um objeto que vive e morre dentro de uma única rota Astro; quando a página da sua rota for renderizada, locals deixará de existir e um novo objeto será criado. Informações que precisarem ser persistidas através de múltiplas páginas devem ser armazenadas em outro lugar.

sequence()

Uma função que aceita funções de middleware como argumentos, e os executará na ordem que foram passados.

createContext

Uma API de baixo nível para criar um APIContext que pode ser utilizado pelo middleware Astro.

Essa função pode ser usada por integrações/adaptadores para executar programaticamente os middlewares Astro.

trySerializeLocals

Uma API de baixo nível que recebe qualquer valor e tenta retornar uma versão serializada (uma string) desse valor. Se o valor não puder ser serializado, essa função irá lançar um erro de runtime.