Skip to content

Deno Fresh 后端 API

HaloLight Deno 后端 API 基于 Fresh 框架和 Deno KV 构建,采用 Deno 原生运行时,提供高性能的 RESTful API 服务。

API 文档https://halolight-deno.h7ml.cn/docs

GitHubhttps://github.com/halolight/halolight-deno

特性

  • 🔐 JWT 双令牌 - Access Token + Refresh Token,自动续期
  • 🛡️ RBAC 权限 - 基于角色的访问控制,通配符匹配
  • 📡 RESTful API - 标准化接口设计,OpenAPI 文档
  • 💾 Deno KV - 内置键值存储,无需外部数据库
  • Islands 架构 - 部分水合,极致性能
  • 数据验证 - 请求参数校验,错误处理
  • 📊 日志系统 - 请求日志,错误追踪
  • 🐳 Docker 支持 - 容器化部署

技术栈

技术版本说明
Deno2.x运行时 (内置 TypeScript)
Fresh1.xDeno 原生 Web 框架
Preact10.x轻量级 UI 库
Deno KV-内置键值存储 (数据库)
Hono4.xAPI 路由框架 (可选)
JWT-身份认证
Tailwind CSS3.x原子化 CSS

快速开始

环境要求

  • Deno >= 2.0.0

安装

bash
# 克隆仓库
git clone https://github.com/halolight/halolight-deno.git
cd halolight-deno

# 无需安装依赖,Deno 自动管理

环境变量

bash
cp .env.example .env
env
# API 配置
API_URL=/api
USE_MOCK=true

# JWT 密钥
JWT_SECRET=your-super-secret-key
JWT_ACCESS_EXPIRES=15m
JWT_REFRESH_EXPIRES=7d

# Deno KV (可选,默认使用本地)
DENO_KV_PATH=./data/kv.db

# 服务配置
PORT=8000
NODE_ENV=development

# 演示账号
DEMO_EMAIL=admin@halolight.h7ml.cn
DEMO_PASSWORD=123456
SHOW_DEMO_HINT=false

# 品牌配置
APP_TITLE=Admin Pro
BRAND_NAME=Halolight

数据库初始化

bash
# Deno KV 无需迁移,自动创建
# 如需种子数据
deno task seed

启动服务

bash
# 开发模式
deno task dev

# 生产模式
deno task build
deno task start

访问 http://localhost:8000

项目结构

halolight-deno/
├── routes/                  # 路由处理
│   ├── api/                 # API 端点
│   │   ├── auth/            # 认证路由
│   │   ├── users/           # 用户路由
│   │   ├── dashboard/       # 仪表盘路由
│   │   ├── documents/       # 文档路由
│   │   ├── files/           # 文件路由
│   │   ├── messages/        # 消息路由
│   │   ├── notifications/   # 通知路由
│   │   └── calendar/        # 日历路由
│   ├── (auth)/              # 认证页面
│   │   ├── login.tsx
│   │   ├── register.tsx
│   │   └── forgot-password.tsx
│   └── (dashboard)/         # 仪表盘页面
│       ├── index.tsx
│       ├── users.tsx
│       └── settings.tsx
├── utils/                   # 工具函数
│   ├── auth.ts              # 认证工具
│   ├── kv.ts                # Deno KV 封装
│   ├── permissions.ts       # 权限检查
│   ├── jwt.ts               # JWT 工具
│   └── validation.ts        # 数据验证
├── islands/                 # 交互式组件 (客户端)
│   ├── AuthProvider.tsx
│   ├── ThemeToggle.tsx
│   └── Dashboard.tsx
├── components/              # UI 组件
│   ├── ui/
│   ├── layout/
│   └── dashboard/
├── static/                  # 静态资源
├── fresh.gen.ts             # Fresh 生成文件
├── fresh.config.ts          # Fresh 配置
├── deno.json                # Deno 配置
└── import_map.json          # 导入映射

API 模块

认证相关端点

方法路径描述权限
POST/api/auth/login用户登录公开
POST/api/auth/register用户注册公开
POST/api/auth/refresh刷新令牌公开
POST/api/auth/logout退出登录需认证
POST/api/auth/forgot-password忘记密码公开
POST/api/auth/reset-password重置密码公开

用户管理端点

方法路径描述权限
GET/api/users获取用户列表users:view
GET/api/users/:id获取用户详情users:view
POST/api/users创建用户users:create
PUT/api/users/:id更新用户users:update
DELETE/api/users/:id删除用户users:delete
GET/api/users/me获取当前用户需认证

完整端点清单

文档管理 (Documents) - 5 个端点

方法路径描述
GET/api/documents获取文档列表
GET/api/documents/:id获取文档详情
POST/api/documents创建文档
PUT/api/documents/:id更新文档
DELETE/api/documents/:id删除文档

文件管理 (Files) - 5 个端点

方法路径描述
GET/api/files获取文件列表
GET/api/files/:id获取文件详情
POST/api/files/upload上传文件
PUT/api/files/:id更新文件信息
DELETE/api/files/:id删除文件

消息管理 (Messages) - 5 个端点

方法路径描述
GET/api/messages获取消息列表
GET/api/messages/:id获取消息详情
POST/api/messages发送消息
PUT/api/messages/:id/read标记已读
DELETE/api/messages/:id删除消息

通知管理 (Notifications) - 4 个端点

方法路径描述
GET/api/notifications获取通知列表
PUT/api/notifications/:id/read标记已读
PUT/api/notifications/read-all全部已读
DELETE/api/notifications/:id删除通知

日历管理 (Calendar) - 5 个端点

方法路径描述
GET/api/calendar/events获取日程列表
GET/api/calendar/events/:id获取日程详情
POST/api/calendar/events创建日程
PUT/api/calendar/events/:id更新日程
DELETE/api/calendar/events/:id删除日程

仪表盘 (Dashboard) - 6 个端点

方法路径描述
GET/api/dashboard/stats统计数据
GET/api/dashboard/visits访问趋势
GET/api/dashboard/sales销售数据
GET/api/dashboard/pie饼图数据
GET/api/dashboard/tasks待办任务
GET/api/dashboard/calendar今日日程

认证机制

JWT 双令牌

Access Token:  15 分钟有效期,用于 API 请求
Refresh Token: 7 天有效期,用于刷新 Access Token

请求头

http
Authorization: Bearer <access_token>

刷新流程

typescript
// utils/jwt.ts
import { create, verify } from "https://deno.land/x/djwt@v3.0.0/mod.ts";

const key = await crypto.subtle.generateKey(
  { name: "HMAC", hash: "SHA-256" },
  true,
  ["sign", "verify"],
);

export async function createAccessToken(userId: string): Promise<string> {
  return await create(
    { alg: "HS256", typ: "JWT" },
    { userId, exp: Date.now() + 15 * 60 * 1000 }, // 15分钟
    key,
  );
}

export async function refreshToken(refreshToken: string): Promise<string> {
  const payload = await verify(refreshToken, key);
  return await createAccessToken(payload.userId as string);
}

权限系统

角色定义

角色说明权限
super_admin超级管理员* (所有权限)
admin管理员users:*, documents:*, files:*, messages:*, calendar:*
user普通用户documents:view, files:view, messages:view, calendar:view
guest访客dashboard:view

权限格式

{resource}:{action}

示例:
- users:view      # 查看用户
- users:create    # 创建用户
- users:*         # 用户所有操作
- *               # 所有权限

权限检查实现

typescript
// utils/permissions.ts
export function hasPermission(user: User, permission: string): boolean {
  const userPermissions = user.permissions || [];

  return userPermissions.some((p) => {
    if (p === "*") return true;
    if (p.endsWith(":*")) {
      const resource = p.slice(0, -2);
      return permission.startsWith(resource + ":");
    }
    return p === permission;
  });
}

// 路由中间件
export function requirePermission(permission: string) {
  return async (req: Request, ctx: any) => {
    const user = ctx.state.user;
    if (!hasPermission(user, permission)) {
      return new Response("Forbidden", { status: 403 });
    }
    return await ctx.next();
  };
}

错误处理

错误响应格式

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数验证失败",
    "details": [
      { "field": "email", "message": "邮箱格式不正确" }
    ]
  }
}

错误码

状态码错误码说明
400VALIDATION_ERROR参数验证失败
401UNAUTHORIZED未授权
403FORBIDDEN无权限
404NOT_FOUND资源不存在
409CONFLICT资源冲突
500INTERNAL_ERROR服务器错误

常用命令

bash
# 开发
deno task dev              # 启动开发服务器 (热重载)

# 构建
deno task build            # 构建生产版本

# 测试
deno test                  # 运行测试
deno test --coverage       # 测试覆盖率

# 数据库
deno task seed             # 初始化种子数据

# 代码质量
deno lint                  # 代码检查
deno fmt                   # 代码格式化
deno check **/*.ts         # 类型检查

部署

Docker

bash
docker build -t halolight-deno .
docker run -p 8000:8000 halolight-deno

Docker Compose

bash
docker-compose up -d
yaml
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      - NODE_ENV=production
      - JWT_SECRET=${JWT_SECRET}
      - DENO_KV_PATH=/data/kv.db
    volumes:
      - deno_kv_data:/data
    restart: unless-stopped

volumes:
  deno_kv_data:

Deno Deploy (推荐)

bash
# 安装 deployctl
deno install -Arf jsr:@deno/deployctl

# 部署到 Deno Deploy
deployctl deploy --project=halolight-deno main.ts

生产环境配置

env
NODE_ENV=production
JWT_SECRET=your-production-secret-key-min-32-chars
DENO_KV_PATH=/data/kv.db
PORT=8000

测试

运行测试

bash
deno test                  # 运行所有测试
deno test --coverage       # 生成覆盖率报告
deno test --watch          # 监听模式

测试示例

typescript
// routes/api/auth/_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/assert/mod.ts";

Deno.test("POST /api/auth/login - success", async () => {
  const response = await fetch("http://localhost:8000/api/auth/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      email: "admin@halolight.h7ml.cn",
      password: "123456",
    }),
  });

  assertEquals(response.status, 200);
  const data = await response.json();
  assertEquals(data.success, true);
  assertEquals(typeof data.data.accessToken, "string");
});

Deno.test("POST /api/auth/login - invalid credentials", async () => {
  const response = await fetch("http://localhost:8000/api/auth/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      email: "wrong@example.com",
      password: "wrong",
    }),
  });

  assertEquals(response.status, 401);
  const data = await response.json();
  assertEquals(data.success, false);
});

性能指标

基准测试

指标数值说明
请求吞吐量~45,000 req/sDeno 2.0, 单核, wrk 测试
平均响应时间<5ms本地 Deno KV, 无外部依赖
内存占用~30MB启动后基础内存
CPU 使用率<10%空闲状态

可观测性

日志系统

typescript
// utils/logger.ts
import { Logger } from "https://deno.land/std@0.208.0/log/mod.ts";

const logger = new Logger("app");

export function logRequest(req: Request, res: Response, duration: number) {
  logger.info(
    `${req.method} ${new URL(req.url).pathname} ${res.status} ${duration}ms`,
  );
}

export function logError(error: Error, context?: any) {
  logger.error(`Error: ${error.message}`, { error, context });
}

健康检查

typescript
// routes/api/health.ts
export const handler = async (req: Request): Promise<Response> => {
  try {
    // 检查 Deno KV 连接
    const kv = await Deno.openKv();
    await kv.get(["health"]);
    await kv.close();

    return Response.json({
      status: "healthy",
      timestamp: new Date().toISOString(),
      uptime: Deno.memoryUsage(),
    });
  } catch (error) {
    return Response.json(
      { status: "unhealthy", error: error.message },
      { status: 503 },
    );
  }
};

监控指标

typescript
// utils/metrics.ts
const metrics = {
  requests: 0,
  errors: 0,
  responseTime: [] as number[],
};

export function recordRequest(duration: number) {
  metrics.requests++;
  metrics.responseTime.push(duration);
}

export function recordError() {
  metrics.errors++;
}

export function getMetrics() {
  const avg = metrics.responseTime.reduce((a, b) => a + b, 0) /
    metrics.responseTime.length;
  return {
    totalRequests: metrics.requests,
    totalErrors: metrics.errors,
    avgResponseTime: avg || 0,
  };
}

常见问题

Q:Deno KV 数据如何持久化?

A:通过配置 DENO_KV_PATH 环境变量指定数据文件路径。

bash
# .env
DENO_KV_PATH=./data/kv.db
typescript
// 使用自定义路径
const kv = await Deno.openKv(Deno.env.get("DENO_KV_PATH"));

Q:如何启用远程 Deno KV (Deno Deploy)?

A:在 Deno Deploy 上部署时,使用 Deno.openKv() 会自动连接到托管的分布式 KV。

typescript
// 生产环境自动使用远程 KV
const kv = await Deno.openKv();

Q:如何处理文件上传?

A:使用 Fresh 的 FormData API 处理文件上传。

typescript
// routes/api/files/upload.ts
export const handler = async (req: Request): Promise<Response> => {
  const formData = await req.formData();
  const file = formData.get("file") as File;

  if (!file) {
    return Response.json({ error: "No file uploaded" }, { status: 400 });
  }

  // 保存文件到 Deno KV 或云存储
  const bytes = await file.arrayBuffer();
  const kv = await Deno.openKv();
  await kv.set(["files", crypto.randomUUID()], {
    name: file.name,
    type: file.type,
    size: file.size,
    data: new Uint8Array(bytes),
  });

  return Response.json({ success: true });
};

Q:Islands 架构如何与 API 集成?

A:Islands 是客户端交互组件,通过 fetch 调用后端 API。

typescript
// islands/UserList.tsx
import { useSignal } from "@preact/signals";
import { useEffect } from "preact/hooks";

export default function UserList() {
  const users = useSignal([]);

  useEffect(() => {
    fetch("/api/users")
      .then((res) => res.json())
      .then((data) => users.value = data);
  }, []);

  return (
    <div>
      {users.value.map((user) => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

开发工具

推荐插件/工具

  • Deno VSCode Extension - 官方 VS Code 插件,提供智能提示和调试
  • deployctl - Deno Deploy 命令行工具
  • wrk / autocannon - HTTP 压力测试工具
  • Deno Lint - 内置代码检查工具

与其他后端对比

特性Deno FreshNestJSFastAPISpring Boot
语言TypeScript (Deno)TypeScriptPythonJava
ORMDeno KVPrismaSQLAlchemyJPA
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
学习曲线⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
启动时间<100ms~2s~1s~5s
内存占用30MB80MB50MB150MB
部署Deno DeployDocker/云Docker/云Docker/云

相关链接