solidstart-middleware-auth
SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.
Best use case
solidstart-middleware-auth is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.
Teams using solidstart-middleware-auth should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/solidstart-middleware-auth/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How solidstart-middleware-auth Compares
| Feature / Agent | solidstart-middleware-auth | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# SolidStart Middleware, Sessions & Auth
## Middleware
Configure in `app.config.ts`:
```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 `event.locals` (trace IDs, feature flags, auth hints)
- 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
```ts
// 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
- `onRequest`: Before route handler (modify headers, store in locals)
- `onBeforeResponse`: After route handler (modify response, logging)
Returning a `Response` from either hook short-circuits the pipeline and skips
remaining middleware and route handlers.
```ts
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`:
```ts
// 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
```ts
// global.d.ts
/// <reference types="@solidjs/start/env" />
declare module "App" {
interface RequestEventLocals {
user?: { id: string; name: string };
startTime?: number;
}
}
```
### Headers & Cookies
```ts
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.
```ts
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.
```ts
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.
```ts
// 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");
}
];
```
```ts
// 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
```ts
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
```ts
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
```ts
// 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
```ts
// 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
```ts
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
```tsx
// 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
```tsx
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
```tsx
const logoutAction = action(async () => {
"use server";
const session = await useAppSession();
await session.clear();
throw redirect("/");
}, "logout");
```
## WebSocket Endpoints
**Experimental** - use with caution.
### Configuration
```ts
// 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
```ts
// 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
1. Keep middleware lightweight - no heavy computation
2. Use sessions for auth state - secure, encrypted cookies
3. Use `deferStream: true` for protected routes
4. Type your locals for better TypeScript support
5. Don't rely on middleware for authorization (doesn't run on all requests)
6. Return a `Response` only when you want to terminate the pipeline
7. Compose small middleware functions and keep ordering explicitRelated Skills
convex-component-authoring
How to create, structure, and publish self-contained Convex components with proper isolation, exports, and dependency management
agentuity-cli-auth-login
Login to the Agentuity Platform using a browser-based authentication flow. Use for managing authentication credentials
agent-command-authoring
Create Claude Code slash commands and OpenCode command files that delegate to subagents. Use when creating new commands or refactoring existing ones to follow the delegation pattern.
agent-authoring
Guide for authoring specialized AI agents. Use when creating, updating, or improving agents, choosing models, defining focus areas, configuring tools, or learning agent best practices.
agentuity-cli-project-auth-init
Set up Agentuity Auth for your project. Requires authentication. Use for managing authentication credentials
agentuity-cli-auth-ssh-list
List all SSH keys on your account. Requires authentication. Use for managing authentication credentials
agentuity-cli-auth-ssh-delete
Delete an SSH key from your account. Requires authentication. Use for managing authentication credentials
agentuity-cli-auth-ssh-add
Add an SSH public key to your account (reads from file or stdin). Requires authentication. Use for managing authentication credentials
agentuity-cli-auth-org-unselect
Clear the default organization preference. Use for managing authentication credentials
agentuity-cli-auth-org-current
Show the current default organization. Use for managing authentication credentials
agentuity-cli-auth-machine-setup
Set up machine authentication by uploading a public key for self-hosted infrastructure. Requires authentication. Use for managing authentication credentials
two-factor-authentication-best-practices
This skill provides guidance and enforcement rules for implementing secure two-factor authentication (2FA) using Better Auth's twoFactor plugin.