Skip to content

Middleware

Since Frog is built on top of Hono, Frog supports Hono's middleware pattern.

Middleware works before and after the .frame handler by allowing you to manipulate the request and response before and after dispatching respectively. We recommend checking out the Hono documentation on Middleware for a more in-depth understanding.

Overview

There are two ways to inject middleware:

  1. Globally, on the App-level.
  2. Isolated, on a Route-level.

App-level

We can inject middleware on the app level using the .use method. This means middleware will be injected for all routes under app.

import { Button, Frog } from 'frog'
import { neynar, type NeynarVariables } from 'frog/middlewares'
 
export const app = new Frog({ title: 'Frog Frame' })
↓ 1. Inject middleware onto the app.
.use( neynar({ apiKey: 'NEYNAR_FROG_FM', features: ['interactor', 'cast'], }), )
2. All routes on `app` will inject the middleware.

Route-level

We can inject middleware on a route level:

import { Button, Frog } from 'frog'
import { neynar } from 'frog/middlewares'
 
const neynarMiddleware = neynar({
  apiKey: 'NEYNAR_FROG_FM',
  features: ['interactor', 'cast'],
})
 
export const app = new Frog({ title: 'Frog Frame' })
 
↓ Inject middleware onto the route.
app.frame('/', neynarMiddleware, (c) => { return c.res({ image: ( <div style={{ color: 'white', display: 'flex', fontSize: 60 }}> {c.var.interactor?.displayName} </div> ), }) })

Built-in Middlewares

Neynar

Frog comes with a built-in middleware for Neynar which allows you to easily integrate Neynar features (such as the interactor of your frame, and frame cast) into Frog context.

import { Button, Frog } from 'frog'
import { neynar } from 'frog/middlewares'
 
export const app = new Frog({ title: 'Frog Frame' })
↓ 1. Inject `neynar` middleware onto the app.
.use( neynar({ apiKey: 'NEYNAR_FROG_FM', features: ['interactor', 'cast'], }), ) .frame('/', (c) => {
↓ 2. Use `c.var` to access Neynar variables!
const { displayName, followerCount } = c.var.interactor || {} console.log('cast: ', c.var.cast) console.log('interactor: ', c.var.
interactor?: { activeStatus: "active" | "inactive"; custodyAddress: string; displayName: string; fid: number; followerCount: number; followingCount: number; object: "user"; pfpUrl: string; profile: { ...; }; username: string; verifications: string[]; verifiedAddresses: { ...; }; viewerContext?: { ...; } | undefined; } | undefined

The user who interacted with the frame.

interactor
)
return c.res({ image: ( <div style={{ alignItems: 'center', color: 'white', display: 'flex', justifyContent: 'center', fontSize: 48, height: '100%', width: '100%', }} > Greetings {displayName}, you have {followerCount} followers. </div> ), }) })

Custom Middleware

You can write your own Frog middleware. This is great if you want to share common logic across or frames or if you are developing a SDK for Frog users to hook into their frames.

import { Frog } from 'frog'
 
export const app = new Frog({ title: 'Frog Frame' })
 
// Custom logger
app.use(async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next()
})
 
// Add a custom header
app.use('/foo/*', async (c, next) => {
  await next()
  c.header('x-message', 'Only called for `/foo` and `/foo/bar` frames.')
})
 
app.frame('/', (c) => {/* ... */})
 
app.frame('/foo', (c) => {/* ... */})
 
app.frame('/foo/bar', (c) => {/* ... */})

Community Middleware

Middleware is one of the most powerful pieces of Frog. This section showcases community-built middleware that you can use in your Frog apps.

If you've built a middleware for Frog, feel free to submit a PR to add it to this list.

Airstack Middlewares

Pinata FDK Middlewares

XMTP Frames Middleware

The XMTP community has implemeted ways to enhance user experience by supporting frames within XMTP applications.

Metadata

To build a Frame with XMTP, you must first add XMTP metadata. This is done following the specifications of OpenFrames.

import { Frog } from 'frog'
 
const addMetaTags = (client: string, version?: string) => {
  // Follow the OpenFrames meta tags spec
  return {
    unstable_metaTags: [
      { property: `of:accepts`, content: version || "vNext" },
      { property: `of:accepts:${client}`, content: version || "vNext" },
    ],
  };
};
 
export const app = new Frog(addMetaTags("xmtp"));
## Errors were thrown in the sample, but not included in an error tag These errors were not marked as being expected: 2345. Expected: // @errors: 2345 Compiler Errors: index.tsx [2345] 347 - Argument of type '{ unstable_metaTags: { property: string; content: string; }[]; }' is not assignable to parameter of type 'FrogConstructorParameters<Env, "/", unknown>'. Property 'title' is missing in type '{ unstable_metaTags: { property: string; content: string; }[]; }' but required in type 'Required<Pick<FrameResponse, "title">>'.

Validate incoming messages:

Install the @xmtp/frames-validator package to validate incoming messages.

npm
npm install @xmtp/frames-validator

Add the middleware to validate incoming messages.

/** @jsxImportSource frog/jsx */
// ---cut---
import { validateFramesPost } from "@xmtp/frames-validator";
import { Frog } from 'frog'
import type { MiddlewareHandler } from 'hono'
 
const addMetaTags = (client: string, version?: string) => {
  // Follow the OpenFrames meta tags spec
  return {
    unstable_metaTags: [
      { property: `of:accepts`, content: version || "vNext" },
      { property: `of:accepts:${client}`, content: version || "vNext" },
    ],
  };
};
 
function xmtpSupport(): MiddlewareHandler<{
  Variables: { client?: 'xmtp' | 'farcaster'; verifiedWalletAddress?: string }
}> {
  return async (c, next) => {
    // Check if the request is a POST and relevant for XMTP processing
    if (c.req.method === "POST") {
      const requestBody = (await c.req.json().catch(() => {})) || {};
      if (requestBody?.clientProtocol?.includes("xmtp")) {
        c.set("client", "xmtp");
        const { verifiedWalletAddress } = await validateFramesPost(requestBody);
        c.set("verifiedWalletAddress", verifiedWalletAddress);
      } else {
        // Add farcaster check
        c.set("client", "farcaster");
      }
    }
    await next();
  }
}
 
const app = new Frog(addMetaTags("xmtp")).use(xmtpSupport());
 
// Access verified wallet address in a frame:
app.frame("/", (c) => {
  /* Get Frame variables */
  const { buttonValue, inputText, status } = c;
 
  // XMTP verified address
  const { verifiedWalletAddress } = c?.var || {};
 
  return c.res({
    image: (
      <div tw="flex">
        XMTP Frame. Verified Address: {verifiedWalletAddress}
      </div>
    )
  })
})
## Errors were thrown in the sample, but not included in an error tag These errors were not marked as being expected: 2307 2345. Expected: // @errors: 2307 2345 Compiler Errors: index.tsx [2307] 81 - Cannot find module '@xmtp/frames-validator' or its corresponding type declarations. [2345] 1186 - Argument of type '{ unstable_metaTags: { property: string; content: string; }[]; }' is not assignable to parameter of type 'FrogConstructorParameters<Env, "/", unknown>'. Property 'title' is missing in type '{ unstable_metaTags: { property: string; content: string; }[]; }' but required in type 'Required<Pick<FrameResponse, "title">>'.

For more information refer to XMTP Docs.