快速开始
安装 Bun(>= 1.0.0):
curl -fsSL https://bun.sh/install | bashmkdir my-ventostack-appcd my-ventostack-appbun init -ybun add @ventostack/core创建 src/main.ts:
import { createApp, createRouter } from "@ventostack/core";
const router = createRouter();
router.get("/", async (ctx) => { return ctx.json({ message: "Hello, VentoStack!" });});
router.get("/users/:id", async (ctx) => { const { id } = ctx.params; return ctx.json({ id, name: "Alice" });});
// 使用类型标记,ctx.params.id 自动推导为 numberrouter.get("/items/:id<int>", async (ctx) => { return ctx.json({ id: ctx.params.id, type: typeof ctx.params.id });});
const app = createApp({ port: 3000 });app.use(router);await app.listen();
console.log("Server running at http://localhost:3000");启动开发服务器
Section titled “启动开发服务器”bun --hot src/main.ts访问 http://localhost:3000,你会看到:
{ "message": "Hello, VentoStack!" }import { createApp, createRouter } from "@ventostack/core";import type { Middleware } from "@ventostack/core";
// 日志中间件const logger: Middleware = async (ctx, next) => { const start = Date.now(); const response = await next(); console.log(`${ctx.method} ${ctx.path} - ${Date.now() - start}ms`); return response;};
// 认证中间件示例const auth: Middleware = async (ctx, next) => { const token = ctx.headers.get("authorization"); if (!token) { return ctx.json({ error: "Unauthorized" }, 401); } return next();};
const router = createRouter();router.get("/protected", async (ctx) => { return ctx.json({ data: "secret" });}, auth);
const app = createApp({ port: 3000 });app.use(logger);app.use(router);await app.listen();完整示例:带数据库和认证
Section titled “完整示例:带数据库和认证”首先安装额外依赖:
bun add @ventostack/database @ventostack/auth @ventostack/cache @ventostack/observabilityimport { createApp, createRouter, requestLogger, errorHandler } from "@ventostack/core";import { createDatabase, defineModel, column } from "@ventostack/database";import { createJWT, createRBAC, createPasswordHasher } from "@ventostack/auth";import { createCache, createMemoryAdapter } from "@ventostack/cache";import { createLogger } from "@ventostack/observability";
// 定义数据模型const UserModel = defineModel("users", { id: column.bigint({ primary: true, autoIncrement: true }), email: column.varchar({ length: 255, unique: true }), password: column.varchar({ length: 255 }), name: column.varchar({ length: 255 }), role: column.varchar({ length: 50 }),});
// 初始化依赖const logger = createLogger({ level: "info" });
// 传入 url 即可自动使用 Bun.sql,无需手动配置 executorconst db = createDatabase({ url: "sqlite://data/abc.db" });
const jwt = createJWT({ secret: process.env.JWT_SECRET || "please_change_me_please_change_me", defaultOptions: { expiresIn: 7 * 24 * 60 * 60 }, // 7 天});
const passwordHasher = createPasswordHasher();const cache = createCache(createMemoryAdapter());const rbac = createRBAC();
// 定义权限角色rbac.addRole({ name: "admin", permissions: [ { resource: "users", action: "read" }, { resource: "users", action: "write" }, { resource: "users", action: "delete" }, ],});rbac.addRole({ name: "user", permissions: [{ resource: "users", action: "read" }],});
const router = createRouter();
// 注册路由router.post("/auth/register", async (ctx) => { const { email, password, name } = await ctx.request.json() as { email: string; password: string; name: string; };
const existing = await db.query(UserModel).where("email", "=", email).get(); if (existing) { return ctx.json({ error: "Email already registered" }, 409); }
const passwordHash = await passwordHasher.hash(password); const user = await db .query(UserModel) .insert({ email, password: passwordHash, name, role: "user" }, { returning: true });
return ctx.json({ id: user?.id, email, name }, 201);});
// 登录路由router.post("/auth/login", async (ctx) => { const { email, password } = await ctx.request.json() as { email: string; password: string };
const user = await db.query(UserModel).where("email", "=", email).get(); if (!user) { return ctx.json({ error: "Invalid credentials" }, 401); }
const valid = await passwordHasher.verify(password, user.password); if (!valid) { return ctx.json({ error: "Invalid credentials" }, 401); }
const token = await jwt.sign({ sub: String(user.id), role: user.role as string }); return ctx.json({ token });});
// 受保护的路由router.get("/users", async (ctx) => { const token = ctx.headers.get("authorization")?.replace("Bearer ", ""); if (!token) { return ctx.json({ error: "Unauthorized" }, 401); }
const payload = await jwt.verify(token);
if (!rbac.can([payload.role as string], "users", "read")) { return ctx.json({ error: "Forbidden" }, 403); }
const users = await cache.remember("users:all", 300, async () => { return db.query(UserModel).select("id", "name", "email").list(); });
return ctx.json(users);});
const app = createApp({ port: 3000 });app.use(errorHandler());app.use(requestLogger());app.use(router);
// 启动前自动建表(示例用;生产环境应使用迁移工具)await db.raw(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE, password TEXT, name TEXT, role TEXT )`);
app.lifecycle.onAfterStart(() => { logger.info("Server started", { port: 3000 });});
await app.listen();