Backend
Middleware
Middleware
Middleware in Shapeless lets you add reusable logic that runs between procedure calls and handlers. It’s ideal for cross-cutting concerns like authentication, logging, and error handling.
Basic Middleware Structure
// resources/shapeless.ts
const myMiddleware = app.middleware(async ({ c, next }) => {
// 1️⃣ Code that runs before the handler
// ...
// 2️⃣ Pass data to the next middleware or handler
return await next({ customData: "value" })
})
Common Use Cases
Authentication Middleware
This middleware ensures the user is authenticated before proceeding. If not authenticated, it throws an error and blocks the procedure:
// resources/shapeless.ts
import { HTTPException } from "hono/http-exception"
import { shapeless } from "@shapelesss/core"
interface Env {
Bindings: { DATABASE_URL: string }
}
export const app = shapeless.init<Env>()
const authMiddleware = app.middleware(async ({ c, next }) => {
// Mock authentication check
const isAuthenticated = true
if (!isAuthenticated) {
throw new HTTPException(401, {
message: "Unauthorized, please sign in.",
})
}
// Attach user info to context for downstream handlers
return await next({ user: { name: "John Doe" } })
})
export const publicProcedure = app.procedure
export const privateProcedure = publicProcedure.use(authMiddleware)
Now, in any privateProcedure
, you can safely access the user info:
// resources/routers/post-router.ts
import { app, privateProcedure } from "../shapeless"
export const postRouter = app.router({
list: privateProcedure.get(({ c, ctx }) => {
// Access user info injected by middleware
const { user } = ctx
return c.json({ posts: [] })
}),
})
Middleware Chaining
You can chain multiple middlewares using .use()
:
const enhancedProcedure = publicProcedure
.use(authMiddleware)
.use(loggingMiddleware)
.use(rateLimitMiddleware)
If middleware depends on previous middleware outputs, use Shapeless’s type inference utility to type ctx
correctly:
// resources/shapeless.ts
import { InferMiddlewareOutput, shapeless } from "@shapelesss/core"
interface Env {
Bindings: { DATABASE_URL: string }
}
export const app = shapeless.init<Env>()
const authMiddleware = app.middleware(async ({ c, next }) => {
return await next({ user: { name: "John Doe" } })
})
type AuthOutput = InferMiddlewareOutput<typeof authMiddleware>
const loggingMiddleware = app.middleware(async ({ c, ctx, next }) => {
const { user } = ctx as AuthOutput
const start = performance.now()
await next()
const end = performance.now()
console.log(`${user.name}'s request took ${end - start}ms`)
})
export const publicProcedure = app.procedure
export const privateProcedure = publicProcedure
.use(authMiddleware)
.use(loggingMiddleware)
Using Hono Middleware
Shapeless is compatible with Hono middleware using the fromHono
adapter:
import { app } from "../shapeless"
import { cors } from "hono/cors"
const corsMiddleware = app.fromHono(cors())
const procedureWithCors = publicProcedure.use(corsMiddleware)
Best Practices
- Keep middleware focused on a single responsibility.
- Handle errors globally with your
appRouter
'sonError()
method.
Common Middleware Examples
- Authentication
- Request logging
- Rate limiting
- Error handling
- Input validation
- Performance monitoring
- CORS handling
→ See all built-in Hono middleware in the Hono docs.