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

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,
};

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

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

On this page