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.

  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>

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

Seção intitulada 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
});
};

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

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().

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.

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.

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.

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

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.

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.