扩展 BFF Server
部分应用中,开发者可能希望对所有 BFF 函数做统一的处理,例如鉴权、日志、数据处理等。
Modern.js 提供了两种方式,允许开发者根据运行时框架自由扩展 BFF Server。
中间件
开发者可以在编写 api/_app.ts
文件中编写中间件,用来扩展 BFF Server。以 Express 作为运行时框架为例,介绍如何手写一个中间件,添加权限校验:
api/_app.ts
import { hook } from '@modern-js/runtime/server';
import { Request, Response, NextFunction } from 'express';
export default hook(({ addMiddleware }) => {
addMiddleware(async (req: Request, res: Response, next: NextFunction) => {
if (req.url !== '/api/login') {
const sid = req?.cookies?.sid;
if (!sid) {
res.status(400);
res.json({ code: -1, message: 'need login' });
} else {
next();
}
} else {
next();
}
});
});
然后添加一个普通的 BFF 函数 api/lambda/hello.ts
:
api/lambda/hello.ts
export default async () => {
return 'Hello Modern.js';
};
接下来,在前端 src/routes/page.tsx
添加接口的访问代码,直接使用一体化的方式调用:
src/routes/page.tsx
import { useState, useEffect } from 'react';
import { get as hello } from '@api/hello';
export default () => {
const [text, setText] = useState('');
useEffect(() => {
async function fetchMyApi() {
const { message } = await hello();
setText(message);
}
fetchMyApi();
}, []);
return <p>{text}</p>;
};
现在运行 dev
命令启动项目,访问 http://localhost:8080/
会发现 /api/hello
的请求被拦截了:
最后修改前端代码 src/routes/page.tsx
,在访问 /api/hello
前先调用登录接口:
import { useState, useEffect } from 'react';
import { get as hello } from '@api/hello';
import { post as login } from '@api/login';
export default () => {
const [text, setText] = useState('');
useEffect(() => {
async function fetchAfterLogin() {
const { code } = await login();
if (code === 0) {
const { message } = await hello();
setText(message);
}
}
fetchAfterLogin();
}, []);
return <p>{text}</p>;
};
刷新页面,可以看到 /api/hello
访问成功:
以上代码模拟了在 /api/_app.ts
中添加中间件的方式,实现了简易的登录功能。同样,可以在这个钩子文件中实现其他功能来扩展 BFF Server。
INFO
不同运行时框架中,中间件的写法不一定相同,详情可见运行时框架。
定义 Server 实例
除了中间件之外,还可以通过 api/app.ts
文件来定义 BFF Server 的实例。开发者需要默认导出一个能被运行时框架插件识别的实例。这里简单展示一下 Koa 和 Express 如何定义 Server 实例。
api/app.ts
import express from 'express';
const app = express();
app.use(async (req, res, next) => {
console.info(`access url: ${req.url}`);
next();
});
export default app;
在复杂的 BFF 逻辑中,定义 Server 实例可以更方便通过一个入口来组织代码逻辑,设计目录结构。在这个文件中,可以执行初始化逻辑,添加全局中间件,声明路由,甚至扩展原有框架。
BFF 函数定义的路由会在 app.ts
文件定义的路由之后注册,所以在这里你也可以拦截 BFF 函数定义的路由,进行预处理或是提前响应。
NOTE
此时,如果应用中同时存在 api/_app.ts
,则定义的中间件会被放在 api/app.ts
导出实例的最后执行。多数情况下,中间件就能覆盖大多数 BFF 函数的定制需求。只有当应用的服务端逻辑比较复杂时,才需要自定义 Server 实例。
CAUTION
当应用中没有 api/app.ts
的时候,Modern.js 默认会添加 koa-body。当应用中存在 api/app.ts
时,如果开发者希望使用带有 Body 的 BFF 函数,需要自行添加 koa-body
。