Nue 边缘优先 HTTP 服务:Nueserver API 完全指南
【免费下载链接】nueFastest way to build modern websites项目地址: https://gitcode.com/GitHub_Trending/nu/nue
Nueserver 是 Nue 项目(Fastest way to build modern websites)中为边缘部署而设计的极简 HTTP 服务器,允许开发者在本地以 CloudFlare Workers 的编程模式编写业务接口,代码无需改造即可在未来直接部署到边缘节点。本文基于官方文档 server-api.md,结合 nueserver.js 源码与测试用例,系统讲解其路由、上下文对象、中间件与错误处理等全部 API,让你读完即可用 Nueserver 写出可运行、可测试、面向边缘部署的服务端代码。
Nueserver 是什么
Nueserver 是一个"边缘优先"(edge-first)的 HTTP 服务器。传统开发流程通常是:本地用 Node.js 全功能运行时开发,部署到边缘时再逐一排查兼容性问题(例如把 bcrypt 换成 Web Crypto、把 ORM 换成原生 SQL、为数据库搭建边缘代理)。Nueserver 反其道而行之——本地开发环境从一开始就使用与边缘兼容的编程模式,开发方式即部署方式。
它借鉴了 Hono 的简洁 API 风格,但做了几处关键差异化设计(详见 nueserver/README.md):
- 全局方法:无需 import,直接在代码中使用
get()、post()、use()等全局函数注册路由; - 只返回 JSON/文本:只提供
c.json()与c.text(),HTML 生成交给前端层; - 不负责静态文件服务:静态资源由构建系统处理,服务层各司其职;
- 不做复杂路由:只有简单模式(静态、参数、通配符),与 CloudFlare 的路由能力一一对应,没有正则路由和复杂参数校验;
- 线性中间件:中间件按注册顺序线性执行,通过显式
next()传递控制权,流程可预测、易调试。
快速开始
在任意.js文件中直接声明路由,无需任何导入语句:
get('/api/users', async (c) => { return c.json([{ id: 1, name: 'Alice' }]) }) post('/api/users', async (c) => { const user = await c.req.json() return c.json(user, 201) })这段代码定义了两个接口:GET/api/users返回用户列表,POST/api/users接收 JSON 请求体并以 201 状态码返回创建结果。从 nueserver.js 的源码可以看到,get/post/del是挂载在globalThis上的全局函数,它们只是把{ method, path, handler }压入全局routes数组:
globalThis.get = (path, handler) => { routes.push({ method: 'GET', path, handler }) }而use()注册的中间件不携带 method 字段,这正是代码中区分普通路由与中间件的关键标识(!route.method即中间件)。
路由处理器
get(path, handler)
处理 GET 请求,支持路径参数:
get('/users', async (c) => { return c.json(users) }) get('/users/:id', async (c) => { const id = c.req.param('id') const user = users.find(u => u.id == id) return c.json(user) })post(path, handler)
处理 POST 请求,通常用于创建资源:
post('/users', async (c) => { const data = await c.req.json() const user = createUser(data) return c.json(user, 201) })del(path, handler)
处理 DELETE 请求:
del('/users/:id', async (c) => { const id = c.req.param('id') deleteUser(id) return c.json({ deleted: id }) })use(path, middleware)
注册在路由处理器之前执行的中间件。use有两种签名:带路径前缀的局部中间件,以及只传一个函数的全局中间件(此时内部自动把 path 设为'*'):
use('/admin/*', async (c, next) => { const auth = c.req.header('authorization') if (!auth) return c.json({ error: 'Unauthorized' }, 401) await next() }) // Global middleware use(async (c, next) => { console.log(c.req.method, c.req.url) await next() })路由模式
静态路由
get('/users', handler) get('/api/status', handler)参数路由
:name形式捕获路径片段,可通过c.req.param('name')读取:
get('/users/:id', handler) // /users/123 get('/posts/:slug/comments', handler) // /posts/hello/comments通配符
*放在路径末尾匹配任意数量的后续路径片段:
use('/admin/*', middleware) // Matches /admin/users, /admin/settings get('/files/*', handler) // Matches any path under /files匹配规则的源码级细节
路由匹配由 matchPath 函数 实现,其行为在 route.test.js 中被系统验证,几个关键语义值得注意:
- 长度校验:带尾部通配符时,请求路径必须比模式(去掉
*后)更长;无通配符时路径段数必须完全相等。因此/admin/*匹配/admin/users和/admin/users/123/profile,但不匹配/admin本身; - 参数捕获:
/users/:id/posts/:postId匹配/users/123/posts/456得到{ id: '123', postId: '456' }; - 全局通配:单独的
'*'匹配任意路径; - 多余段数不匹配:
/users/:id不匹配/users/123/extra。
上述规则全部有对应测试用例,例如['/admin/*', '/admin', false]与['/users/:id/*', '/users/123/posts/456', true, { id: '123' }],可以直接在 packages/nueserver 目录运行bun test复现。
上下文对象(Context)
每个处理器都会收到一个上下文对象c,它封装了请求读取与响应构造的完整能力(createContext实现见 nueserver.js)。
请求对象(c.req)
get('/example', async (c) => { // Get route parameters const id = c.req.param('id') // Get query parameters const page = c.req.query('page') // single param const params = c.req.query() // all params as object // Get headers const auth = c.req.header('authorization') // Parse request body const data = await c.req.json() // JSON const text = await c.req.text() // plain text })c.req是标准 Request 对象的轻量封装:query(key)基于URLSearchParams实现——传 key 时返回单个值,不传时遍历返回全部参数对象;json()/text()直接委托给req.json()/req.text();header(key)委托给req.headers.get(key);param(key)读取当前请求匹配到的路径参数(内部通过_params挂载)。
响应助手(c)
get('/example', async (c) => { // JSON response return c.json({ message: 'Hello' }) // JSON with status return c.json({ error: 'Not found' }, 404) // Text response return c.text('Hello world') // Status then JSON return c.status(201).json({ created: true }) })源码中的默认值与链式语义如下(nueserver.js):
c.json(data, status = 200):Response.json(data, { status }),第二个参数缺省为 200;c.text(text, status = 200):new Response(text, { status });c.status(status).json(data):返回一个带固定状态码的链式 json 调用。
环境对象(c.env)
c.env用于访问环境专属资源。当前本地开发阶段主要支持 CloudFlare 请求头的本地模拟:
post('/contact', async (c) => { // CloudFlare headers (mocked locally) const country = c.req.header('cf-ipcountry') const ip = c.req.header('cf-connecting-ip') const data = await c.req.json() return c.json({ ...data, country, ip }) })在 Nuekit 的开发服务器中,这些 CF 头由 worker.js 的getCFHeaders()模拟,包括cf-ipcountry(FI)、cf-ipcity(Helsinki)、cf-connecting-ip(127.0.0.1)、cf-timezone等十余个真实边缘环境会提供的头。将来部署到 CloudFlare Workers 后,这些头将提供真实的网络与地理位置数据。
官方文档还预告了c.env的未来形态——业务模型抽象:
// Coming: business model primitives const { customers, leads, charges } = c.env get('/api/customers', async (c) => { const all = await customers.all() return c.json(all) })实际上,Nuekit 当前已经实现了雏形:见 model.js 的createEnv——它会扫描@shared/server/data/目录下的 JSON 文件,为每个文件(如users.json、leads.json)生成带getAll()、create()、get()等方法的数据模型挂载到env上,users.json还会额外获得login/logout/authenticate会话能力(会话持久化在.nue/sessions.json)。
中间件模式
中间件接收(c, next),await next()将控制权交给后续处理器,返回值会成为响应。
认证
use('/api/*', async (c, next) => { const token = c.req.header('authorization') if (!isValid(token)) { return c.json({ error: 'Invalid token' }, 401) } await next() })CORS
use(async (c, next) => { const response = await next() response.headers.set('Access-Control-Allow-Origin', '*') return response })日志
use(async (c, next) => { const start = Date.now() const response = await next() console.log(`${c.req.method} ${c.req.url} - ${Date.now() - start}ms`) return response })从 nueserver.js 的fetch实现可以看到中间件的执行语义:所有路由按注册顺序线性遍历,普通路由与中间件都只执行第一个返回 Response 的匹配项,其后立即返回——这意味着中间件与路由的处理顺序就是注册顺序,行为可预测。
错误处理
处理器中抛出的异常会自动转换为 500 响应,无需手动 try/catch:
get('/might-fail', async (c) => { // This error becomes a 500 response throw new Error('Something went wrong') })对应的实现位于 nueserver.js:整个请求处理被 try/catch 包裹,捕获到错误时打印Server error:日志并返回500 Internal Server Error。
自定义错误码则显式返回响应对象:
get('/users/:id', async (c) => { const user = findUser(c.req.param('id')) if (!user) { return c.json({ error: 'User not found' }, 404) } return c.json(user) })此外,若所有路由都没有匹配,服务器返回404 Not Found(nueserver.js),该行为在 server.test.js 中有对应测试。
开发工作流:与 Nuekit 的集成
Nueserver 的路由是全局函数,不需要任何 import,可以在任何位置定义:
// Define routes anywhere get('/health', async (c) => { return c.json({ status: 'ok' }) }) // Use middleware use('/admin/*', requireAuth) // Handle different methods post('/webhook', handleWebhook) del('/cache/:key', clearCache)服务器负责其余所有事情。同一套代码在本地通过nue serve开发,未来部署到 CloudFlare Workers 时无需改写。
在 Nuekit 中,集成链路是完整的:
- 开发服务器启动时,serve.js 调用
getServer(conf?.server)获取后端处理器; - server/index.js 根据配置选择代理或本地 worker;
- worker.js 从
@shared/server/index.js(即你存放 Nueserver 路由代码的文件)导入路由,通过routes.length = 0清空旧路由后重新 import 实现热重载,并用matches()预判请求是否命中后端路由; - 命中的请求会被包装成标准
Request,带上模拟的 CloudFlare 头后交给 Nueserver 的fetch()处理。
启动时控制台会输出Backend server started with N routes,直观确认后端路由加载成功。full模板(packages/templates/full/@shared/server)中带有可直接参考的server/index.js与data/示例数据。
安装与测试
对于实际项目,推荐通过 Nuekit 获得完整开发体验:
bun install --global nuekit也可以把 Nueserver 作为独立库安装(npm 包名为nue-edgeserver,见 package.json):
bun install nue-edgeserver仓库中提供了完整的测试套件用于验证行为:
- route.test.js:用 13 组用例覆盖路径匹配的静态/参数/通配符/长度校验逻辑;
- server.test.js:以真实
Request驱动fetch(),验证 GET/POST、路由参数、带认证中间件的 401/200 行为以及 404 兜底。
在 packages/nueserver 目录下运行bun test即可执行全部测试。
总结
Nueserver 用不到 150 行的实现(nueserver.js)提供了面向边缘部署的完整服务端能力:全局函数式路由、参数与通配符匹配、携带请求/响应助手的上下文对象、线性中间件,以及自动化的 404/500 错误处理。配合 Nuekit 的开发服务器,它在本地就能以 CloudFlare Workers 的模式编写和调试接口,为 Nue 项目后续的边缘部署愿景打下基础。更详细的设计动机与"边缘优先"理念可继续阅读 Nueserver 文档。
【免费下载链接】nueFastest way to build modern websites项目地址: https://gitcode.com/GitHub_Trending/nu/nue
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考