MuseMVP

MuseMVP Base: Next.js for Node and Cloudflare. MuseMVP Flare: an AI SaaS template that runs entirely on Cloudflare. Authentication, billing and administration included.

Product
  • Home
  • Pricing
  • Templates
Resources
  • Docs
  • Blog
  • Changelog
Legal
  • Privacy
  • Terms
  • Community
Open-Launch Top 1 Daily WinnerOpen-Launch Top 1 Daily WinnerVerified DR - Verified Domain Rating for musemvp.comVerified DR - Verified Domain Rating for musemvp.comMuseMVP - Featured on Startup FameMuseMVP - Featured on Startup FameFazier badgeFazier badgeLiftOff award badgeLiftOff award badge

© 2026 MuseMVP. All rights reserved.

MuseMVP
  • Pricing
  • Docs
  • Blog
  • Changelog
Buy MuseMVP Base
MuseMVP Docs
MuseMVP Docs
OverviewTech StackQuick Start
Project Structure
API ArchitectureBackend GuideDefining New RoutesAPI Keys
EmailObject Storage
Internationalization
SEOAnalytics
API

Defining New Routes

Step-by-step guide on how to create a new API endpoint with validation and OpenAPI metadata.

This guide walks you through adding a new API route in MuseMVP, following the existing patterns for validation, middleware, and OpenAPI documentation.

Step-by-Step Flow

Write failing tests first; update schema/queries in packages/db/src/ with db first, then bind them in app database wrappers. Orchestration belongs in src/modules/<feature>/lib/.

Create a new Hono router or extend an existing one in src/backend/api/routes/.

Register the router in src/backend/api/app.ts.

Add API client hooks in src/backend/api-client/ for frontend consumption.


1. Define the Route Handler

Create a new router file or add to an existing one. Use Zod for validation and describeRoute for OpenAPI metadata.

/** New file: src/backend/api/routes/example/router.ts */
import { Hono } from "hono";
import { describeRoute, resolver, validator } from "hono-openapi";
import { z } from "zod";
import { authMiddleware } from "@/backend/api/middleware/auth";

const querySchema = z.object({
  limit: z.coerce.number().int().min(1).max(100).default(10),
  offset: z.coerce.number().int().min(0).default(0),
});

const responseSchema = z.object({
  items: z.array(z.object({ id: z.string(), name: z.string() })),
  total: z.number(),
});

export const exampleRouter = new Hono()
  .basePath("/example")
  .use(authMiddleware)
  .get(
    "/",
    validator("query", querySchema),
    describeRoute({
      tags: ["Example"],
      summary: "List items",
      responses: {
        200: {
          description: "Items returned",
          content: {
            "application/json": {
              schema: resolver(responseSchema),
            },
          },
        },
      },
    }),
    async (c) => {
      const { limit, offset } = c.req.valid("query");
      const user = c.get("user");
      /** Call the owning feature service using validated input. */
      return c.json({ items: [], total: 0 });
    },
  );

2. Register the Router

Add the new router to the main app in src/backend/api/app.ts:

import { exampleRouter } from "./routes/example/router";

const appRouter = app
  .route("/", authRouter)
  .route("/", exampleRouter);
/** Preserve the existing route registrations. */

3. Validation Patterns

LocationUse CaseExample
validator("query", schema)Query paramsPagination, search
validator("json", schema)JSON bodyCreate/update payloads
validator("form", schema)Form dataContact form, file upload
validator("param", schema)Path params/conversations/:id

4. Middleware Options

MiddlewareWhen to Use
authMiddlewareRequires logged-in user; sets c.get("user") and c.get("session")
adminMiddlewareRequires admin role; use for admin-only endpoints
localeMiddlewareSets c.get("locale") from request/cookie for i18n

5. API Client Hook

Create a TanStack Query hook that calls your new endpoint:

/** New file: src/backend/api-client/example/use-example.ts */
import { useQuery } from "@tanstack/react-query";
import { apiClient } from "@/backend/api-client/api-client";

export function useExample(params?: { limit?: number; offset?: number }) {
  return useQuery({
    queryKey: ["example", params],
    queryFn: async () => {
      const query = {
        ...(params?.limit !== undefined ? { limit: String(params.limit) } : {}),
        ...(params?.offset !== undefined ? { offset: String(params.offset) } : {}),
      };
      const res = await apiClient.example.$get({ query });
      if (!res.ok) throw new Error("Fetch failed");
      return res.json();
    },
  });
}

Quick Reference

src/backend/api/routes/admin/users.ts
src/backend/api/routes/contact/router.ts
src/backend/api/routes/assets/router.ts
  • Admin route: admin/users.ts — adminMiddleware, query validation, describeRoute
  • Public route: contact/router.ts — localeMiddleware, form validation
  • Auth route: assets/router.ts — authMiddleware, query + json validation, describeRoute

Add unit/API contracts for unauthenticated access, permissions, invalid input, and errors. Update tests/e2e/SCENARIOS.md for new browser journeys, synchronize app/API copies, and run pnpm apps:parity.

Backend Guide

A practical guide to src/backend responsibilities, api-client usage in client/server contexts, and the full request chain.

API Keys

Create, view, test, and revoke API Keys in the settings center for programmatic access.

On this page

Step-by-Step Flow1. Define the Route Handler2. Register the Router3. Validation Patterns4. Middleware Options5. API Client HookQuick Reference

One payment. Tax included.

MuseMVP Base

Next.js for Node and for Cloudflare, with auth, payments and admin.

$69

Buy MuseMVP Base