Flowcore Hono API 构建器
OpenTeleometry Track-Log 关联
追踪和记录的关联性, 启动命令至关重要数字 :
QQ 正确的设置
// main.ts - FIRST THING IN APPLICATION
import init from "@flowcore/hono-api/otel"
// Initialize OTEL before any other imports
init({
otelServiceName: "my-api",
otelEndpoint: "http://localhost:4318", // OTLP HTTP endpoint
runtime: "bun" // or "node" | "deno"
})
// Now safe to import and use the library
import { HonoApi, HonoApiRouter, loggerFactory } from "@flowcore/hono-api"
import { z } from "@hono/zod-openapi"
// Create logger with trace correlation
const createLogger = loggerFactory({
prettyPrintLogs: true,
logLevel: "info"
}).createLogger
const logger = createLogger("main")
// Your logs will now include trace_id and span_id automatically
logger.info("Application starting", { port: 3000 })
常见错误
// WRONG: Creating logger before OTEL initialization
import { loggerFactory } from "@flowcore/hono-api"
const logger = loggerFactory(...).createLogger("main") // No trace correlation!
import init from "@flowcore/hono-api/otel"
init({ ... }) // Too late - logger already created
环境变量
| 变量 | 类型 | 说明 | 默认 | 需求 |
|---|---|---|---|---|
| OTEL 服务 NAME | 字符串 | 追踪服务名称 | - 说吧 | ✓ |
| OTEL 出口 OTLP ENDPOINT | 字符串 | OTLP 端点 URL | - 说吧 | ✓ |
全球JWT配置
支持 HonoApi 全球JWT配置 用于自定义的 OIDC 供应商,如 Keycloak, Okta, 或 Auth0. 这就不需要每个路由配置JWT验证.
QQ 全球 JWT 配置
import { HonoApi, JWTValidationConfig, FLOWCORE_JWT_CONFIG } from "@flowcore/hono-api"
import { z } from "@hono/zod-openapi"
// Custom JWT configuration for Keycloak
const keycloakConfig: JWTValidationConfig = {
extractUserId: (payload) => payload.sub as string,
extractEmail: (payload) => payload.email as string,
extractIsAdmin: (payload) => {
const roles = payload.realm_access?.roles || []
return roles.includes("admin")
},
validatePayload: (payload) => {
if (!payload.sub) {
throw new Error("Missing subject in JWT")
}
if (!payload.email_verified) {
throw new Error("Email not verified")
}
}
}
// Option 1: Global config via auth.jwtConfig
const api = new HonoApi({
auth: {
jwks_url: "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/certs",
jwtConfig: keycloakConfig
}
})
// Option 2: Global config via authDefaults (takes precedence)
const api = new HonoApi({
auth: {
jwks_url: "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/certs",
},
authDefaults: {
jwtConfig: keycloakConfig,
optional: true // Apply to routes without explicit auth config
}
})
JWT 配置接口
interface JWTValidationConfig {
extractUserId: (payload: JWTPayload) => string
extractEmail?: (payload: JWTPayload) => string | undefined
extractIsAdmin?: (payload: JWTPayload) => boolean
validatePayload?: (payload: JWTPayload) => void
}
OIDC 提供方实例
Keycloak 配置 :
const keycloakConfig: JWTValidationConfig = {
extractUserId: (payload) => payload.sub as string,
extractEmail: (payload) => payload.email as string,
extractIsAdmin: (payload) => {
const realmRoles = payload.realm_access?.roles || []
return realmRoles.includes("admin")
}
}
Auth0 配置 :
const auth0Config: JWTValidationConfig = {
extractUserId: (payload) => payload.sub as string,
extractEmail: (payload) => payload.email as string,
extractIsAdmin: (payload) => {
const roles = payload["https://myapp.com/roles"] || []
return roles.includes("admin")
}
}
Okta 配置 :
const oktaConfig: JWTValidationConfig = {
extractUserId: (payload) => payload.uid as string,
extractEmail: (payload) => payload.email as string,
extractIsAdmin: (payload) => {
const groups = payload.groups || []
return groups.includes("Administrators")
}
}
福利
- ✅ 没有每道配置 - JWT配置适用于全球
- ✅ OIDC 供应商的灵活性 - 支持任何JWT发行商
- ✅ 后向兼容性 - Flowcore 认证在默认情况下仍然有效
- ✅ 自定义验证 - 在JWT验证中添加商业逻辑
- ✅ 基于角色的接入 - 从JWT索赔中提取管理/用户角色
使用量
import { HonoApi, HonoApiRouter } from "../src/mod.ts"
import { z } from "@hono/zod-openapi"
// root-router.ts
const rootRouter = new HonoApiRouter()
rootRouter.get("/health", {
tags: ["root"],
auth: {
optional: true,
},
output: z.object({
status: z.enum(["ok", "error"]).openapi({
example: "ok",
}),
}),
handler: () => {
return {
status: "ok" as const,
}
},
})
// data-cores-router.ts
const apiV1DataCoreRouter = new HonoApiRouter()
apiV1DataCoreRouter.post("/", {
summary: "Create a data core",
description: "Create a data core",
tags: ["data-core"],
auth: {
permissions: (input) => {
return [{
action: "write",
resource: [`frn::${input.body.tenantId}:data-core:*`],
}]
},
},
input: {
body: z.object({
name: z.string().openapi({
example: "my-data-core",
}),
tenantId: z.string().openapi({
example: "00000000-0000-0000-0000-000000000000",
description: "The ID of the tenant to create the data core on",
}),
}),
},
output: z.object({
id: z.string(),
name: z.string(),
tenantId: z.string(),
}),
handler: (input) => {
return {
id: crypto.randomUUID(),
name: input.body.name,
tenantId: input.body.tenantId,
}
},
})
// Hono API
const honoApi = new HonoApi({
// Auth is optional (These are the defaults)
auth: {
jwks_url: "https://auth.flowcore.io/realms/flowcore/protocol/openid-connect/certs",
api_key_url: "https://iam.api.flowcore.io",
iam_url: "https://iam.api.flowcore.io",
},
// OpenAPI is optional (These are the defaults)
openapi: {
docPath: "/swagger",
jsonPath: "/swagger/openapi.json",
version: "0.0.1",
name: "Hono API",
description: "Hono API",
},
// Logger is optional (This is the default)
logger: console,
})
honoApi.addRouter("/", rootRouter)
honoApi.addRouter("/api/v1/data-cores", apiV1DataCoreRouter)
// Serve your api in your preferred manner
Deno.serve({ port: 3000 }, honoApi.app.fetch)
Bun.serve({
port: 3000,
fetch: honoApi.app.fetch
})
测试
该项目包括对API功能的全面测试:
# Run all tests
deno task test
# Run tests in watch mode
deno task test:watch
# Run tests with coverage
deno task test:coverage
# Type check all files
deno task typecheck
测试范围
测试套房包括:
- HonoApi 类: 初始化、配置、路由器安装、请求处理
- 胡诺阿皮鲁特级: 路由注册,路径处理,HTTP方法,配置
- 例外类别: 所有有适当状态代码和序列化的错误类型
- 请求/答复处理获取, POST, PUT, PATCH, DEELETE 方法
- 输入验证: 标题、查询参数、路径参数、请求机构
- OpenAPI 文档: 提供光谱生成和文献服务
- 普罗米修斯量子: 计量收集和端点保护
- 处理错误: 内部服务器错误和自定义例外
认证测试按要求排除在外,但必要时可以单独添加.