☰
LoopBack 4 与 Express 组合应用实战:将 LoopBack REST API 挂载到 Express 应用
2026/10/14 3:09:12 网站建设 项目流程
  • 后端
  • 微服务

【免费下载链接】loopback-next

LoopBack makes it easy to build modern API applications that require complex integrations.

项目地址:https://gitcode.com/gh_mirrors/lo/loopback-next
点击查看免费下载

导读

本文基于 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 提供了三步快速体验流程:

  1. 运行lb4 example命令,选择并克隆express-composition仓库:

    lb4 example express-composition
  2. 进入示例目录:

    cd loopback4-example-express-composition
  3. 启动应用:

    $ 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 test

pretest钩子会先执行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; }

构造函数完成三件事:

  1. 挂载 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 对外提供。

  2. 自定义 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!'); });
  3. 静态资源托管:

    // 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.

项目地址:https://gitcode.com/gh_mirrors/lo/loopback-next
点击查看免费下载
上一篇:Tasmota 中的 MCP2515 CAN 总线库:从 SPI 驱动原理到收发实战
下一篇:使用 Zeek 日志分析检测横向移动:从 SMB 管理共享到 NTLM 口令喷洒的实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询