Skip to Content
AgentPress is finally here! šŸŽ‰

Routing Architecture

This document describes the routing implementation for both frontend and backend applications in the AgentPress monorepo.

Overview

AgentPress uses a dual routing architecture:

  • Frontend: TanStack React Router with Vite
  • Backend: Hono with modular route organization

Frontend Routing (TanStack React Router)

Applications

AppPortPurposeRoute File
Console3000Admin dashboardapps/console/src/routes.tsx
Portal3002User-facing chat interfaceapps/portal/src/routes.tsx
Drop-in Chat3003Embeddable iframe widgetapps/dropin-chat/src/routes.tsx

Router Initialization

All frontend apps follow the same initialization pattern in main.tsx:

import { createRouter, RouterProvider } from "@tanstack/react-router"; import { routeTree } from "./routes"; const router = createRouter({ routeTree }); declare module "@tanstack/react-router" { interface Register { router: typeof router; } } ReactDOM.createRoot(document.getElementById("root")!).render( <RouterProvider router={router} /> );

Root Route Pattern

Each application wraps routes in providers:

const rootRoute = createRootRoute({ component: () => ( <AgentPressProvider> <Outlet /> <ReactQueryDevtools /> <Toaster /> </AgentPressProvider> ), }); export const routeTree = rootRoute.addChildren( createAppRoutes(rootRoute) as any, );

Configuration

Routes require configuration to be set before creating the route tree:

const config = { apiHost: import.meta.env.VITE_API_HOST || "http://localhost:3001", toolsUI, voiceTranscription: true, appType: EAppType.CONSOLE, // or PORTAL }; setAgentPressConfig(config); // Must be called BEFORE creating routes

Console Route Structure

The console app uses dynamic route generation via createAppRoutes():

/ -> redirects to /dashboard/agents Public Routes (PublicLayout - no sidebar): /login /invite?token=... /reset-password?token=... Protected Routes (ConsoleLayout - with sidebar): /dashboard/ /agents /agents/create /agents/$id /analytics /conversations /conversations/$threadId /evaluations /evaluations/$id /evaluations/$id/edit /feedback /knowledge /knowledge/$documentId /mcps /personas /playground /settings /telemetry /tools /tools/create /tools/$id /users /tasks

Portal Route Structure

The portal app supports extensive customization via query parameters:

const chatSearchSchema = z.object({ agentId: z.string().optional().default(""), showThreadList: z.boolean().optional().default(true), showUsername: z.boolean().optional().default(true), showAgentSelector: z.boolean().optional().default(true), enableUpload: z.boolean().optional().default(true), sidebarOpen: z.boolean().optional().default(true), showNewThreadButton: z.boolean().optional().default(true), showFeedback: z.boolean().optional().default(true), showInputField: z.boolean().optional().default(true), showUserHeaders: z.boolean().optional().default(false), showTaskButton: z.boolean().optional().default(true), initialUserMessage: z.string().optional().default(""), authToken: z.string().optional().default(""), providerName: z.string().optional().default(""), threadId: z.string().optional().default(""), }); const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: "/", validateSearch: chatSearchSchema, component: ChatPage, });

Route Guards

Routes use before-load hooks for authentication:

// Public routes redirect authenticated users beforeLoad: async ({ location }) => { await redirectIfAuthenticated(); } // Protected routes require authentication beforeLoad: async ({ location }) => { await requireAuth(); }

Dynamic Routes

Dynamic route parameters use the $ prefix:

// Route: /agents/$id const agentRoute = createRoute({ getParentRoute: () => dashboardRoute, path: "agents/$id", component: AgentDetailPage, }); // Access parameter in component function AgentDetailPage() { const { id } = Route.useParams(); }

Backend Routing (Hono)

Architecture

The backend uses Hono 4.x with a modular route organization pattern optimized for edge deployment.

Server Entry Point

File: apps/api/src/index.ts

export async function startServer({ agentsPath, toolsPath, externalApp, seedAdminUser, }) { const app = createAgentPressApp({ externalApp }); // Initialize registries await agentRegistry.initialize(agentsPath); await mcpRegistry.initialize(); // Set up dependency injection container.setDependencies({ toolRegistry, agentRegistry, pluginRegistry, mcpRegistry, taskRegistry, }); // Start server serve(app); }

Route Registration

File: packages/api/src/hono/app.ts

Routes are organized as modular Hono apps:

export const createAgentPressApp = () => { const app = new Hono(); // Apply global middleware app.use(logger()); app.use(cors()); // Mount routers with prefixes app.route("/auth", authRoutes); app.route("/users", usersRoutes); app.route("/agents", agentsRoutes); app.route("/chat", chatRoutes); app.route("/threads", threadsRoutes); app.route("/orgs", orgsRoutes); return app; };

Route Factory Pattern

All routes export a Hono app:

// packages/api/src/hono/routes/agents.ts export const agentsRoutes = new Hono<AppContext>(); agentsRoutes.use(requireAuth()); agentsRoutes.get("/", async (c) => { const agents = await agentRegistry.getAllAgents(); return c.json(agents); }); agentsRoutes.get("/:id", async (c) => { const { id } = c.req.param(); const agent = await agentRegistry.getAgent(id); return c.json(agent.getAgentPressConfig()); });

Request Validation

Use Zod schemas with Hono’s zValidator:

import { zValidator } from "@hono/zod-validator"; const CreateAgentSchema = z.object({ name: z.string().min(1), description: z.string().optional(), }); agentsRoutes.post( "/", zValidator("json", CreateAgentSchema), async (c) => { const data = c.req.valid("json"); // data is validated and typed return c.json({ success: true }); } );

API Endpoints Reference

Authentication (/auth)

  • GET /auth/github - GitHub OAuth initiation
  • GET /auth/github/callback - GitHub OAuth callback
  • GET /auth/google - Google OAuth initiation
  • GET /auth/google/callback - Google OAuth callback
  • POST /auth/login/credentials - Email/password login
  • POST /auth/logout - User logout

Agents (/agents)

  • GET /agents - List all agents
  • GET /agents/:id - Get agent details
  • POST /agents - Create agent
  • PUT /agents/:id - Update agent
  • DELETE /agents/:id - Delete agent

Chat (/chat)

  • POST /chat - Send message and get streaming response
  • GET /chat/tools - Get available tools

Threads (/threads)

  • GET /threads - Get threads with pagination
  • POST /threads - Create new thread
  • DELETE /threads/:threadId - Delete thread

Middleware

Authentication Middleware

// Require Clerk authentication app.use(clerkAuth()); // Require org role app.use(requireOrgRole(EOrgMemberRole.ADMIN, EOrgMemberRole.OWNER)); // Optional - extract if present app.use(optionalAuth());

Rate Limiting

import { rateLimiter } from "hono-rate-limiter"; // Global rate limit (15 min window, 100 requests) app.use( rateLimiter({ windowMs: 900000, max: 100, }) );

Best Practices

  1. Hono Apps as Routers: Export Hono apps from route files for composition
  2. Middleware Composition: Chain middleware with .use() for clarity
  3. Zod Validation: Use zValidator for all request data validation
  4. Context Types: Define typed context for better IDE support
  5. Before-Load Hooks: Use for authentication guards in frontend routes
Last updated on