RPC templates use Hono's built-in RPC client for seamless type inference from backend to frontend. No code generation needed - types flow automatically.
Available RPC Templates
Full Stack with RPC
Complete application with all features enabled.
npm create vinoflare@latest my-app --rpc
Features:
✅ React frontend with TanStack Router
✅ Hono RPC client with type inference
✅ Cloudflare D1 database with Drizzle ORM
✅ Better Auth with Discord OAuth
✅ Zero code generation
RPC without Authentication
Perfect for public applications with data persistence.
Ideal for simple frontends with minimal backend logic.
npm create vinoflare@latest my-app --rpc --no-db
Features:
✅ React frontend with TanStack Router
✅ Hono RPC client with type inference
❌ No database
❌ No authentication
✅ Minimal configuration
How RPC Works
1. Define Backend Routes
// src/server/modules/todos/todos.routes.tsimport { Hono } from "hono";import { z } from "zod";import { zValidator } from "@hono/zod-validator";const todosApp = new Hono() .get("/", async (c) => { const todos = await db.select().from(todosTable); return c.json(todos); }) .post( "/", zValidator( "json", z.object({ title: z.string(), completed: z.boolean().default(false), }) ), async (c) => { const body = c.req.valid("json"); const todo = await db.insert(todosTable).values(body); return c.json(todo, 201); } ) .delete("/:id", async (c) => { const id = c.req.param("id"); await db.delete(todosTable).where(eq(todosTable.id, id)); return c.json({ success: true }); });export type TodosApp = typeof todosApp;
2. Export App Type
// src/server/core/app-factory.tsexport function createApp() { const app = new Hono() .route("/api/todos", todosApp) .route("/api/users", usersApp); return app;}export type AppType = ReturnType<typeof createApp>;
3. Use in Frontend
// src/client/lib/rpc-client.tsimport { hc } from "hono/client";import type { AppType } from "@/server/core/app-factory";export const client = hc<AppType>("/");
// src/client/routes/todos.tsximport { client } from "@/client/lib/rpc-client";export function TodosPage() { const [todos, setTodos] = useState([]); useEffect(() => { loadTodos(); }, []); const loadTodos = async () => { const res = await client.api.todos.$get(); if (res.ok) { const data = await res.json(); setTodos(data); } }; const createTodo = async (title: string) => { const res = await client.api.todos.$post({ json: { title, completed: false } }); if (res.ok) { await loadTodos(); } }; return ( // Your component JSX );}