LessJS 的服务端层是 Hono。API routes 使用标准 Request/Response 语义, 适合部署到 serverless 或 edge runtime。
API routes 放在 app/routes/api。 模块默认导出一个 Hono app。
// app/routes/api/posts.ts
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => {
return c.json([
{ id: 1, title: 'Hello LessJS' },
]);
});
app.post('/', async (c) => {
const body = await c.req.json();
return c.json({ id: 2, ...body }, 201);
});
export default app;
export type AppType = typeof app;
| File | URL |
|---|---|
| app/routes/api/status.ts | /api/status |
| app/routes/api/posts.ts | /api/posts |
| app/routes/api/users/[id].ts | /api/users/:id |
Hono middleware works normally inside API routes. Validation, auth, rate limits and response shaping should live close to the handler that owns the behavior.
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
const schema = z.object({
title: z.string().min(1),
body: z.string().optional(),
});
app.post('/', zValidator('json', schema), (c) => {
const data = c.req.valid('json');
return c.json({ id: crypto.randomUUID(), ...data }, 201);
});
export default app;
Islands can call API routes with fetch or Hono client helpers. Keep fetch state local unless multiple islands truly need a shared protocol.
async function loadPosts() {
const res = await fetch('/api/posts');
if (!res.ok) throw new Error('Failed to load posts');
return await res.json();
}
SSG output is static files. API routes are part of the generated Hono app, but a purely static host will not run them. Deploy API routes through a serverless adapter or platform function when the app needs runtime behavior.
Near-term LessJS fullstack work should focus on explicit adapters, FormData actions, typed RPC and env/secrets. Until those are stable, API routes are powerful but intentionally simple.