Awesome-omni-skill solidstart-middleware-auth
SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/backend/solidstart-middleware-auth" ~/.claude/skills/diegosouzapw-awesome-omni-skill-solidstart-middleware-auth && rm -rf "$T"
manifest:
skills/backend/solidstart-middleware-auth/SKILL.mdsafety · automated scan (low risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
- references .env files
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content
SolidStart Middleware, Sessions & Auth
Middleware
Configure in
app.config.ts:
export default defineConfig({ middleware: "src/middleware/index.ts" });
When to Use Middleware (and When Not To)
Use middleware for:
- Request/response header management (CSP, cache control, CORS)
- Request-scoped data via
(trace IDs, feature flags, auth hints)event.locals - Early redirects (legacy URLs, locale routing, maintenance mode)
- Lightweight preprocessing (normalizing headers, basic validation)
Avoid middleware for:
- Authorization (middleware does not run on every client-side navigation)
- Heavy computation or database calls (keep it fast and side-effect light)
- Business logic that depends on user state (put checks in queries/actions/API)
Basic Middleware
// src/middleware/index.ts import { createMiddleware } from "@solidjs/start/middleware"; export default createMiddleware({ onRequest: (event) => { console.log("Request:", event.request.url); event.locals.startTime = Date.now(); }, onBeforeResponse: (event) => { const duration = Date.now() - event.locals.startTime; console.log(`Request took ${duration}ms`); } });
Lifecycle Events
: Before route handler (modify headers, store in locals)onRequest
: After route handler (modify response, logging)onBeforeResponse
Returning a
Response from either hook short-circuits the pipeline and skips
remaining middleware and route handlers.
export default createMiddleware({ onRequest: (event) => { event.locals.userAgent = event.request.headers.get("user-agent"); }, onBeforeResponse: (event) => { event.response.headers.set("X-Response-Time", "100ms"); } });
Accessing Locals
Store in middleware, access with
getRequestEvent:
// middleware/index.ts export default createMiddleware({ onRequest: (event) => { event.locals.user = { id: "123", name: "John" }; } }); // In server function import { getRequestEvent } from "solid-js/web"; const getUser = query(async () => { "use server"; const event = getRequestEvent(); return event?.locals?.user; }, "user");
Typing Locals
// global.d.ts /// <reference types="@solidjs/start/env" /> declare module "App" { interface RequestEventLocals { user?: { id: string; name: string }; startTime?: number; } }
Headers & Cookies
import { getCookie, setCookie } from "vinxi/http"; export default createMiddleware({ onRequest: (event) => { // Read headers const userAgent = event.request.headers.get("user-agent"); // Set headers event.request.headers.set("x-custom-header", "value"); event.response.headers.set("Cache-Control", "max-age=3600"); // Cookies const theme = getCookie(event.nativeEvent, "theme"); setCookie(event.nativeEvent, "session", "abc123", { httpOnly: true, secure: true, maxAge: 60 * 60 * 24 }); } });
Custom Responses & Short-Circuiting
Only
Response objects can be returned from middleware.
import { json, redirect } from "@solidjs/router"; export default createMiddleware({ onRequest: (event) => { const { pathname } = new URL(event.request.url); if (pathname === "/old-path") { return redirect("/new-path", 301); } const authHeader = event.request.headers.get("Authorization"); if (!authHeader) { return json({ error: "Unauthorized" }, { status: 401 }); } } });
Building Custom Middleware (Composable Functions)
Prefer small, focused functions and compose them in order.
import type { FetchEvent } from "@solidjs/start/server"; function withRequestId() { return (event: FetchEvent) => { const id = crypto.randomUUID(); event.locals.requestId = id; event.response.headers.set("x-request-id", id); }; } function withTiming() { return (event: FetchEvent) => { event.locals.startTime = Date.now(); }; } function withTimingResponse() { return (event: FetchEvent) => { const ms = Date.now() - event.locals.startTime; event.response.headers.set("x-response-time", `${ms}ms`); }; } export default createMiddleware({ onRequest: [withRequestId(), withTiming()], onBeforeResponse: [withTimingResponse()] });
Extending Existing Middleware
Export shared middleware arrays, then extend them in route-specific configs.
// src/middleware/base.ts import type { FetchEvent } from "@solidjs/start/server"; export const baseOnRequest: Array<(e: FetchEvent) => void> = [ (event) => { event.locals.userAgent = event.request.headers.get("user-agent"); } ];
// src/middleware/index.ts import { createMiddleware } from "@solidjs/start/middleware"; import { baseOnRequest } from "./base"; import type { FetchEvent } from "@solidjs/start/server"; function withMaintenanceMode() { return (event: FetchEvent) => { if (process.env.MAINTENANCE === "true") { return new Response("Maintenance", { status: 503 }); } }; } export default createMiddleware({ onRequest: [...baseOnRequest, withMaintenanceMode()] });
Redirects & Responses
import { redirect, json } from "@solidjs/router"; export default createMiddleware({ onRequest: (event) => { const { pathname } = new URL(event.request.url); if (pathname === "/old-path") { return redirect("/new-path", 301); } const authHeader = event.request.headers.get("Authorization"); if (!authHeader) { return json({ error: "Unauthorized" }, { status: 401 }); } } });
Chaining Middleware
function middleware1(event: FetchEvent) { event.request.headers.set("x-header1", "value1"); } function middleware2(event: FetchEvent) { event.request.headers.set("x-header2", "value2"); } export default createMiddleware({ onRequest: [middleware1, middleware2] });
Order matters: middleware runs in array order; dependencies must come first.
Sessions
Basic Session
// src/lib/session.ts import { useSession } from "vinxi/http"; type SessionData = { userId?: string; theme?: "light" | "dark"; }; export async function useAppSession() { "use server"; const session = await useSession<SessionData>({ password: process.env.SESSION_SECRET!, // Must be 32+ chars name: "app-session" }); return session; } // Generate: openssl rand -base64 32
Session Operations
// Get session data export async function getSessionUserId() { "use server"; const session = await useAppSession(); return session.data.userId; } // Update session export async function updateSession(updates: Partial<SessionData>) { "use server"; const session = await useAppSession(); await session.update(updates); } // Clear session export async function clearSession() { "use server"; const session = await useAppSession(); await session.clear(); }
Using with Queries
const getCurrentUser = query(async () => { "use server"; const session = await useAppSession(); if (!session.data.userId) { throw redirect("/login"); } return await db.getUser(session.data.userId); }, "currentUser");
Authentication
Protected Routes
// routes/admin.tsx import { query, redirect, createAsync } from "@solidjs/router"; const getAdminData = query(async () => { "use server"; const session = await useAppSession(); if (!session.data.userId) { throw redirect("/login"); } const user = await db.getUser(session.data.userId); if (!user.isAdmin) { throw redirect("/"); } return await db.getAdminData(); }, "adminData"); export default function AdminPage() { const data = createAsync(() => getAdminData(), { deferStream: true }); return <div>{data()}</div>; }
Important: Use
deferStream: true - server-side redirects can't occur after streaming starts.
Login Action
const loginAction = action(async (formData: FormData) => { "use server"; const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString(); const user = await db.verifyUser(email, password); if (!user) { return { error: "Invalid credentials" }; } const session = await useAppSession(); await session.update({ userId: user.id }); throw redirect("/dashboard"); }, "login");
Logout Action
const logoutAction = action(async () => { "use server"; const session = await useAppSession(); await session.clear(); throw redirect("/"); }, "logout");
WebSocket Endpoints
Experimental - use with caution.
Configuration
// app.config.ts export default defineConfig({ server: { experimental: { websocket: true } } }).addRouter({ name: "ws", type: "http", handler: "./src/ws.ts", target: "server", base: "/ws" });
WebSocket Handler
// src/ws.ts import { eventHandler } from "vinxi/http"; export default eventHandler({ handler() {}, websocket: { async open(peer) { console.log("Connection:", peer.id); }, async message(peer, msg) { const message = msg.text(); peer.send(message); // Broadcast }, async close(peer, details) { console.log("Closed:", peer.id); }, async error(peer, error) { console.error("Error:", error); } } });
Best Practices
- Keep middleware lightweight - no heavy computation
- Use sessions for auth state - secure, encrypted cookies
- Use
for protected routesdeferStream: true - Type your locals for better TypeScript support
- Don't rely on middleware for authorization (doesn't run on all requests)
- Return a
only when you want to terminate the pipelineResponse - Compose small middleware functions and keep ordering explicit