- 后端
- 微服务
【免费下载链接】loopback-next
LoopBack makes it easy to build modern API applications that require complex integrations.
导读
本文基于 LoopBack 4 官方示例express-composition(examples/express-composition),完整讲解如何把 LoopBack 4 构建的 REST API 作为中间件挂载到独立的 Express 应用中,实现两种框架的混合使用:LoopBack 负责模型、数据源、Repository、Controller 与 OpenAPI 规范,Express 负责门户页面、自定义路由与静态资源托管。读完本文,你将掌握lb4 example拉取示例、ExpressServer门面类的组装、listenOnStart: false与requestHandler的底层原理,以及如何用测试验证这种混合架构。
背景:为什么要把 LoopBack 挂到 Express 上
Express 是一个极度非约定化(un-opinionated)的 Node.js 框架,而 LoopBack 4 则提供了模型驱动、数据源抽象、依赖注入与自动生成 OpenAPI 规范等开箱能力。二者各有优势:
- 以 LoopBack 为宿主:将 Express 应用或路由挂载进 LoopBack 4 应用,适合以 LoopBack 为主体的场景(可参考 Express-middleware 与 Route.md#mounting-an-express-router 中的 "Mounting an Express Router" 章节);
- 以 Express 为门面(facade):把 LoopBack REST API 挂载进 Express 应用,作为中间件使用。本文即此模式——用户在两种框架间自由搭配所需特性,这正是官方教程《Creating an Express Application with LoopBack REST API》(docs/site/express-with-lb4-rest-tutorial.md)的主题。
本示例@loopback/example-express-composition(package.json)演示了后一种组合:一个名为NoteApplication的 LoopBack 4 应用提供/notesCRUD 接口与/ping探测接口,而ExpressServer门面类把它挂载在/api前缀下,同时提供首页、/hello自定义路由和静态文件服务。
环境准备与快速上手
前置条件
根据 README 的说明,运行本示例需要:
- Node.js v10 或更高版本(当前仓库的 package.json 中
engines字段实际要求node: "22 || 24 || 26",以仓库实际约束为准); - 熟悉 JavaScript(ES6)、REST 基本概念;
- 安装 LoopBack 4 CLI 工具:
npm i -g @loopback/cli用 CLI 拉取并运行示例
README 提供了三步快速体验流程:
运行
lb4 example命令,选择并克隆express-composition仓库:lb4 example express-composition进入示例目录:
cd loopback4-example-express-composition启动应用:
$ npm start Server is running at http://127.0.0.1:3000
启动后访问http://127.0.0.1:3000可看到 Express 首页,访问http://127.0.0.1:3000/api/explorer可打开 LoopBack 的 API Explorer。注意 package.json 中start脚本为node .,且prestart会先执行npm run rebuild(清理并重新编译 TypeScript),因此首次npm start会自动完成构建。
运行测试
README 指出,在示例根目录执行:
npm testpretest钩子会先执行npm run rebuild重新编译,随后lb-mocha运行dist/__tests__/**/*.js下的全部验收测试,posttest再执行 lint 检查。
从零搭建:完整教程步骤
官方教程(docs/site/express-with-lb4-rest-tutorial.md)给出了从脚手架到组合完成的逐步指引,仓库中的源码即为教程的最终形态,可逐一对照。
第 1 步:脚手架生成 LoopBack 应用
lb4 app note按提示填写:项目描述为 "An application for recording notes.",应用类名为NoteApplication,并启用 eslint、prettier、mocha、loopbackBuild、vscode、repositories、services 等选项。生成的NoteApplication继承自BootMixin(ServiceMixin(RepositoryMixin(RestApplication))),见 application.ts。
第 2 步:添加 Note 模型
运行lb4 model,以Entity为基类创建Note模型,包含:
id:number类型,主键;title:string类型,必填;content:string类型,可选。
仓库中的最终实现见 note.model.ts,其中@property({type: 'number', id: true})标记主键,@property({type: 'string', required: true})标记必填字段。
第 3 步:创建内存数据源与种子数据
运行lb4 datasource ds创建内存数据源,数据落盘路径为./data/ds.json。仓库中 ds.datasource.ts 的配置为:
const config = { name: 'ds', connector: 'memory', localStorage: '', file: './data/ds.json', };示例自带两条种子数据(data/ds.json),ids.Note记录自增主键计数:
{ "ids": { "Note": 3 }, "models": { "Note": { "1": "{\"title\":\"Things I need to buy\",\"content\":\"milk, cereal, and waffles\",\"id\":1}", "2": "{\"title\":\"Great frameworks\",\"content\":\"LoopBack is a great framework\",\"id\":2}" } } }第 4 步:添加 Repository 与 Controller
- 运行
lb4 repository,选择DsDataSource、Note模型,基类选DefaultCrudRepository。仓库实现见 note.repository.ts,通过@inject('datasources.ds')注入数据源; - 运行
lb4 controller note,类型选 "REST Controller with CRUD functions",id类型为number,HTTP 路径为默认的/notes。完整 CRUD 实现见 note.controller.ts,覆盖POST /notes、GET /notes、GET /notes/count、GET /notes/{id}、PATCH /notes、PATCH /notes/{id}、PUT /notes/{id}、DELETE /notes/{id}八个端点,并使用getModelSchemaRef(Note)自动生成请求/响应体的 JSON Schema。
此外,ping.controller.ts 提供了一个GET /ping探测端点,返回问候语、当前时间、请求 URL 与请求头。
构建门面 Express 应用:核心源码剖析
安装依赖
npm install --save express npm install --save-dev @types/express仓库 package.json 中实际依赖express@^4.22.3与@types/express@^4.17.25。
ExpressServer 门面类
新建src/server.ts(仓库实现见 server.ts),该类同时持有两个应用实例:
export class ExpressServer { public readonly app: express.Application; // Express 门面应用 public readonly lbApp: NoteApplication; // LoopBack 4 应用 private server?: http.Server; }构造函数完成三件事:
挂载 LoopBack 应用:
// Expose the front-end assets via Express, not as LB4 route this.app.use('/api', this.lbApp.requestHandler);这是整个组合模式的关键:
this.lbApp.requestHandler是一个HttpRequestListener(即 Express 中间件形式的请求处理器),把它挂到 Express 的/api前缀下,LoopBack 的所有路由(/api/notes、/api/ping、/api/openapi.json、/api/explorer)便经由 Express 对外提供。自定义 Express 路由:
this.app.get('/', function (_req: Request, res: Response) { res.sendFile(path.join(__dirname, '../public/express.html')); }); this.app.get('/hello', function (_req: Request, res: Response) { res.send('Hello world!'); });静态资源托管:
// Serve static files in the public folder this.app.use(express.static(path.join(__dirname, '../public')));这样
public/下的静态文件(如 express.html、notes.html)可直接通过 URL 访问。
启动逻辑同样需要两步:
public async start() { await this.lbApp.start(); const port = this.lbApp.restServer.config.port ?? 3000; const host = this.lbApp.restServer.config.host ?? '127.0.0.1'; this.server = this.app.listen(port, host); await once(this.server, 'listening'); }先启动 LoopBack 应用完成内部初始化,再由app.listen()让 Express 真正监听端口。boot()负责调用this.lbApp.boot()完成自动装载;stop()则依次停止 LoopBack 应用与关闭 HTTP 服务(利用events模块的once等待监听/关闭事件)。
入口文件与 listenOnStart 配置
入口 index.ts 定义了main(),并在作为主模块运行时执行:
if (require.main === module) { // Run the application const config = { rest: { port: +(process.env.PORT ?? 3000), host: process.env.HOST ?? 'localhost', openApiSpec: { // useful when used with OpenAPI-to-GraphQL to locate your application setServersFromRequest: true, }, // Use the LB4 application as a route. It should not be listening. listenOnStart: false, }, }; main(config).catch(err => { console.error('Cannot start the application.', err); process.exit(1); }); }其中listenOnStart: false是本例的关键配置,注释明确说明:LoopBack 应用被当作 Express 的一条路由使用,不应自行监听端口,端口监听由 Express 门面应用负责。setServersFromRequest: true则让 OpenAPI 规范中的 server 地址按实际请求动态生成,便于配合 OpenAPI-to-GraphQL 等工具定位应用。
底层原理:requestHandler 与 listenOnStart 在 RestServer 中的实现
从源码层面看,requestHandler与listenOnStart均由RestServer实现:
requestHandler是一个惰性初始化的 getter(packages/rest/src/rest.server.ts),首次访问时触发_setupRequestHandlerIfNeeded(),内部创建一个 Express 应用_expressApp并把_requestHandler指向它,再挂载 CORS 中间件、OpenAPI spec 端点与核心请求处理逻辑(_handleHttpRequest),最后注册异常处理器(同文件 L259-L289);listenOnStart在start()方法中被检查(packages/rest/src/rest.server.ts):当值为false时直接返回,不创建也不启动内部HttpServer;其默认值为true(见 DEFAULT_CONFIG),注释明确指出关闭监听适用于"把 LoopBack REST server 作为路由挂到门面 Express 应用"的场景。
这也解释了组合模式下为什么必须先await this.lbApp.start()再app.listen():前者完成路由注册、序列(Sequence)装配等初始化(但并不监听端口),后者才真正对外提供服务。
此外,basePath概念值得注意:RestServer支持通过basePath配置让所有路由挂载到统一前缀(见 rest.server.ts)。本例没有启用basePath,而是由 Express 侧的app.use('/api', ...)完成前缀,两种方式达到的效果类似。
定制应用组件
自定义 Sequence
sequence.ts 定义了自定义 Sequence:
import {MiddlewareSequence} from '@loopback/rest'; export class MySequence extends MiddlewareSequence {}MiddlewareSequence是 LoopBack 4 基于中间件链的默认 Sequence 实现,涵盖 CORS、路由发现、参数解析、认证、执行操作、发送响应与错误处理等环节。示例在 application.ts 中通过this.sequence(MySequence)注册。
生命周期观察者
hello.observer.ts 演示了生命周期观察者:用@lifeCycleObserver()装饰器标记,实现LifeCycleObserver接口,在start()和stop()时分别向events数组推入hello-start/hello-stop事件。应用启动后,该观察者会在 LoopBack 应用的生命周期中被自动调用。
数据库迁移脚本
migrate.ts 提供了 schema 迁移入口:node ./dist/migrate --rebuild会以drop模式重建,不带参数则以alter模式调整。它对内存数据源这类无 schema 概念的场景影响有限,但对 PostgreSQL、MySQL 等关系型连接器是标准做法。
测试验证:验收测试如何校验组合行为
示例的验收测试位于 src/tests/acceptance,测试辅助函数 test-helper.ts 通过new ExpressServer({rest: givenHttpServerConfig()})创建门面应用,再用supertest(server.app)生成 HTTP 客户端。核心断言如下:
- express.acceptance.ts 验证:
GET /返回首页 HTML;GET /notes.html返回静态页并包含<h2>Notes;GET /hello返回Hello world!;GET /api/explorer301 重定向到./explorer/(相对重定向,便于反向代理下正常工作),GET /api/explorer/返回 API Explorer 页面;- 通过
server.lbApp.get('lifeCycleObservers.HelloObserver')获取生命周期观察者,确认start事件已被触发;
- note.acceptance.ts 验证
POST /api/notes创建、GET /api/notes查询以及GET /api/返回 LoopBack 首页。值得注意的是,它在测试中用lbApp.bind('datasources.config.ds').to({name: 'ds', connector: 'memory'})覆盖数据源配置,避免测试数据写入data/ds.json文件; - ping.controller.acceptance.ts 验证
GET /api/ping?msg=world返回{greeting: 'Hello from LoopBack'}。
这些测试直观证明:同一端口上,Express 路由(/、/hello、静态资源)与 LoopBack 路由(/api/*)共存且互不干扰,是混合架构最有力的验证依据。
常见问题与要点总结
- 为何 LoopBack 应用不自己监听端口?因为
listenOnStart: false指示RestServer.start()跳过创建HttpServer(rest.server.ts),端口由门面 Express 的app.listen()统一监听; requestHandler是什么?它是 LoopBack 暴露的 Express 兼容中间件,内部是一个配置好的 Express 应用(rest.server.ts),可直接被app.use('/api', ...)消费;- 如何切换端口/主机?通过环境变量
PORT、HOST注入,入口代码为port: +(process.env.PORT ?? 3000)、host: process.env.HOST ?? 'localhost'; - 静态资源放哪?放
public/目录,由express.static托管;LoopBack 侧也可用this.static('/', ...)挂载静态目录(见 application.ts),但本例刻意交给 Express 处理,体现职责划分; - 反向思维场景:若想反方向——以 LoopBack 为宿主挂载 Express 中间件——可参考 Express-middleware 文档。
至此,你已经掌握了"Express 门面 + LoopBack REST API"这一组合模式的完整落地方法:从 CLI 拉取示例、理解ExpressServer的组装逻辑,到listenOnStart与requestHandler的底层机制,再到用验收测试验证混合路由的可靠性。这套模式非常适合需要"API 快速成型 + 前端定制门户"并存的生产项目。
- 后端
- 微服务
【免费下载链接】loopback-next
LoopBack makes it easy to build modern API applications that require complex integrations.
相关推荐
将 LoopBack 4 REST API 挂载到 Express 应用:express-composition 组合实战指南
将 LoopBack 4 REST API 挂载到 Express 应用:express composition 组合实战指南 导读 LoopBack 4 与
后端微服务LoopBack 3 组件 REST API 端点迁移指南:从 Express 路由到 LoopBack 4 Controller
LoopBack 3 组件 REST API 端点迁移指南:从 Express 路由到 LoopBack 4 Controller 本文是 LoopBack 迁
后端微服务在 LoopBack 4 中挂载 LoopBack 3 应用:@loopback/booter-lb3app 实战指南
在 LoopBack 4 中挂载 LoopBack 3 应用:@loopback/booter lb3app 实战指南 导读 LoopBack 3(LB3)与
后端微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考