@modern-js/main-doc 0.0.0-nightly-20240909025816 → 0.0.0-nightly-20240910170704
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/en/apis/app/hooks/api/lambda.mdx +4 -48
- package/docs/en/apis/app/hooks/api/middleware.mdx +11 -0
- package/docs/en/components/enable-bff.mdx +19 -2
- package/docs/en/components/extend-bff-function.mdx +5 -0
- package/docs/en/configure/app/auto-load-plugin.mdx +4 -0
- package/docs/en/guides/advanced-features/_meta.json +7 -4
- package/docs/en/guides/advanced-features/bff/_meta.json +1 -1
- package/docs/en/guides/advanced-features/bff/extend-server.mdx +154 -0
- package/docs/en/guides/advanced-features/bff/frameworks.mdx +52 -123
- package/docs/en/guides/advanced-features/bff/function.mdx +108 -80
- package/docs/en/guides/advanced-features/bff/sdk.mdx +40 -51
- package/docs/en/guides/advanced-features/build-performance.mdx +2 -2
- package/docs/en/guides/advanced-features/page-performance/_meta.json +1 -0
- package/docs/en/guides/basic-features/render/streaming-ssr.mdx +1 -1
- package/docs/en/guides/basic-features/routes.mdx +1 -1
- package/docs/en/guides/basic-features/static-assets.mdx +1 -1
- package/docs/zh/apis/app/hooks/api/lambda.mdx +5 -48
- package/docs/zh/apis/app/hooks/api/middleware.mdx +11 -0
- package/docs/zh/components/enable-bff.mdx +19 -2
- package/docs/zh/components/extend-bff-function.mdx +5 -0
- package/docs/zh/configure/app/auto-load-plugin.mdx +4 -0
- package/docs/zh/guides/advanced-features/_meta.json +7 -4
- package/docs/zh/guides/advanced-features/bff/_meta.json +1 -1
- package/docs/zh/guides/advanced-features/bff/extend-server.mdx +156 -0
- package/docs/zh/guides/advanced-features/bff/frameworks.mdx +51 -117
- package/docs/zh/guides/advanced-features/bff/function.mdx +69 -59
- package/docs/zh/guides/advanced-features/bff/sdk.mdx +27 -36
- package/docs/zh/guides/advanced-features/build-performance.mdx +2 -2
- package/docs/zh/guides/advanced-features/page-performance/_meta.json +1 -0
- package/docs/zh/guides/advanced-features/rsbuild-plugin.mdx +1 -1
- package/docs/zh/guides/advanced-features/rspack-start.mdx +2 -2
- package/docs/zh/guides/basic-features/alias.mdx +5 -11
- package/docs/zh/guides/basic-features/env-vars.mdx +1 -1
- package/docs/zh/guides/basic-features/static-assets.mdx +1 -1
- package/i18n.json +4 -0
- package/package.json +4 -4
- package/rspress.config.ts +1 -3
- package/docs/en/apis/app/hooks/api/api.mdx +0 -80
- package/docs/en/apis/app/hooks/api/app.mdx +0 -12
- package/docs/en/guides/advanced-features/bff/type.mdx +0 -46
- package/docs/zh/apis/app/hooks/api/api.mdx +0 -81
- package/docs/zh/apis/app/hooks/api/app.mdx +0 -12
- package/docs/zh/guides/advanced-features/bff/type.mdx +0 -46
- /package/docs/en/guides/advanced-features/{bff/index.mdx → bff.mdx} +0 -0
- /package/docs/en/guides/advanced-features/{code-split.mdx → page-performance/code-split.mdx} +0 -0
- /package/docs/en/guides/advanced-features/{inline-assets.mdx → page-performance/inline-assets.mdx} +0 -0
- /package/docs/en/guides/advanced-features/{optimize-bundle.mdx → page-performance/optimize-bundle.mdx} +0 -0
- /package/docs/zh/guides/advanced-features/{bff/index.mdx → bff.mdx} +0 -0
- /package/docs/zh/guides/advanced-features/{code-split.mdx → page-performance/code-split.mdx} +0 -0
- /package/docs/zh/guides/advanced-features/{inline-assets.mdx → page-performance/inline-assets.mdx} +0 -0
- /package/docs/zh/guides/advanced-features/{optimize-bundle.mdx → page-performance/optimize-bundle.mdx} +0 -0
@@ -6,13 +6,16 @@
|
|
6
6
|
"label": "use-bff",
|
7
7
|
"collapsed": true
|
8
8
|
},
|
9
|
-
|
9
|
+
{
|
10
|
+
"type": "dir",
|
11
|
+
"name": "page-performance",
|
12
|
+
"label": "page-performance",
|
13
|
+
"collapsed": true
|
14
|
+
},
|
15
|
+
"build-performance",
|
10
16
|
"compatibility",
|
11
17
|
"eslint",
|
12
18
|
"low-level",
|
13
19
|
"source-build",
|
14
|
-
"build-performance",
|
15
|
-
"inline-assets",
|
16
|
-
"optimize-bundle",
|
17
20
|
"web-server"
|
18
21
|
]
|
@@ -1 +1 @@
|
|
1
|
-
["function", "
|
1
|
+
["function", "frameworks", "extend-server", "sdk"]
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# 扩展 BFF Server
|
2
|
+
|
3
|
+
部分应用中,开发者可能希望对所有 BFF 函数做统一的处理,例如鉴权、日志、数据处理等。
|
4
|
+
|
5
|
+
Modern.js 提供了两种方式,允许开发者根据运行时框架自由扩展 BFF Server。
|
6
|
+
|
7
|
+
## 中间件
|
8
|
+
|
9
|
+
开发者可以在编写 `api/_app.ts` 文件中编写中间件,用来扩展 BFF Server。以 Express 作为运行时框架为例,介绍如何手写一个中间件,添加权限校验:
|
10
|
+
|
11
|
+
```ts title="api/_app.ts"
|
12
|
+
import { hook } from '@modern-js/runtime/server';
|
13
|
+
import { Request, Response, NextFunction } from 'express';
|
14
|
+
|
15
|
+
export default hook(({ addMiddleware }) => {
|
16
|
+
addMiddleware(async (req: Request, res: Response, next: NextFunction) => {
|
17
|
+
if (req.url !== '/api/login') {
|
18
|
+
const sid = req?.cookies?.sid;
|
19
|
+
if (!sid) {
|
20
|
+
res.status(400);
|
21
|
+
res.json({ code: -1, message: 'need login' });
|
22
|
+
} else {
|
23
|
+
next();
|
24
|
+
}
|
25
|
+
} else {
|
26
|
+
next();
|
27
|
+
}
|
28
|
+
});
|
29
|
+
});
|
30
|
+
```
|
31
|
+
|
32
|
+
|
33
|
+
然后添加一个普通的 BFF 函数 `api/lambda/hello.ts`:
|
34
|
+
|
35
|
+
```ts title="api/lambda/hello.ts"
|
36
|
+
export default async () => {
|
37
|
+
return 'Hello Modern.js';
|
38
|
+
};
|
39
|
+
```
|
40
|
+
|
41
|
+
接下来,在前端 `src/routes/page.tsx` 添加接口的访问代码,直接使用一体化的方式调用:
|
42
|
+
|
43
|
+
```ts title="src/routes/page.tsx"
|
44
|
+
import { useState, useEffect } from 'react';
|
45
|
+
import { get as hello } from '@api/hello';
|
46
|
+
|
47
|
+
export default () => {
|
48
|
+
const [text, setText] = useState('');
|
49
|
+
|
50
|
+
useEffect(() => {
|
51
|
+
async function fetchMyApi() {
|
52
|
+
const { message } = await hello();
|
53
|
+
setText(message);
|
54
|
+
}
|
55
|
+
|
56
|
+
fetchMyApi();
|
57
|
+
}, []);
|
58
|
+
|
59
|
+
return <p>{text}</p>;
|
60
|
+
};
|
61
|
+
```
|
62
|
+
|
63
|
+
现在运行 `dev` 命令启动项目,访问 `http://localhost:8080/` 会发现 `/api/hello` 的请求被拦截了:
|
64
|
+
|
65
|
+

|
66
|
+
|
67
|
+
最后修改前端代码 `src/routes/page.tsx`,在访问 `/api/hello` 前先调用登录接口:
|
68
|
+
|
69
|
+
:::note
|
70
|
+
此处没有真实实现登录接口,代码仅作为演示。
|
71
|
+
:::
|
72
|
+
|
73
|
+
```ts
|
74
|
+
import { useState, useEffect } from 'react';
|
75
|
+
import { get as hello } from '@api/hello';
|
76
|
+
import { post as login } from '@api/login';
|
77
|
+
|
78
|
+
export default () => {
|
79
|
+
const [text, setText] = useState('');
|
80
|
+
|
81
|
+
useEffect(() => {
|
82
|
+
async function fetchAfterLogin() {
|
83
|
+
const { code } = await login();
|
84
|
+
if (code === 0) {
|
85
|
+
const { message } = await hello();
|
86
|
+
setText(message);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
fetchAfterLogin();
|
90
|
+
}, []);
|
91
|
+
|
92
|
+
return <p>{text}</p>;
|
93
|
+
};
|
94
|
+
```
|
95
|
+
|
96
|
+
刷新页面,可以看到 `/api/hello` 访问成功:
|
97
|
+
|
98
|
+

|
99
|
+
|
100
|
+
以上代码模拟了在 `/api/_app.ts` 中添加中间件的方式,实现了简易的登录功能。同样,可以在这个钩子文件中实现其他功能来扩展 BFF Server。
|
101
|
+
|
102
|
+
:::info
|
103
|
+
不同运行时框架中,中间件的写法不一定相同,详情可见[运行时框架](/guides/advanced-features/bff/frameworks)。
|
104
|
+
|
105
|
+
:::
|
106
|
+
|
107
|
+
## 定义 Server 实例
|
108
|
+
|
109
|
+
除了中间件之外,还可以通过 `api/app.ts` 文件来定义 BFF Server 的实例。开发者需要默认导出一个能被运行时框架插件识别的实例。这里简单展示一下 Koa 和 Express 如何定义 Server 实例。
|
110
|
+
|
111
|
+
import { Tabs, Tab as TabItem } from "@theme";
|
112
|
+
|
113
|
+
<Tabs>
|
114
|
+
<TabItem value="express" label="Express.js" default>
|
115
|
+
|
116
|
+
```ts title="api/app.ts"
|
117
|
+
import express from 'express';
|
118
|
+
|
119
|
+
const app = express();
|
120
|
+
app.use(async (req, res, next) => {
|
121
|
+
console.info(`access url: ${req.url}`);
|
122
|
+
next();
|
123
|
+
});
|
124
|
+
|
125
|
+
export default app;
|
126
|
+
```
|
127
|
+
|
128
|
+
</TabItem>
|
129
|
+
<TabItem value="koa" label="Koa.js">
|
130
|
+
|
131
|
+
```ts title="api/app.ts"
|
132
|
+
import koa from 'koa';
|
133
|
+
|
134
|
+
const app = new Koa();
|
135
|
+
app.use(async (ctx, next) => {
|
136
|
+
console.info(`access url: ${ctx.url}`);
|
137
|
+
await next();
|
138
|
+
});
|
139
|
+
|
140
|
+
export default app;
|
141
|
+
```
|
142
|
+
|
143
|
+
</TabItem>
|
144
|
+
</Tabs>
|
145
|
+
|
146
|
+
在复杂的 BFF 逻辑中,定义 Server 实例可以更方便通过一个入口来组织代码逻辑,设计目录结构。在这个文件中,可以执行初始化逻辑,添加全局中间件,声明路由,甚至扩展原有框架。
|
147
|
+
|
148
|
+
BFF 函数定义的路由会在 `app.ts` 文件定义的路由之后注册,所以在这里你也可以拦截 BFF 函数定义的路由,进行预处理或是提前响应。
|
149
|
+
|
150
|
+
:::note
|
151
|
+
此时,如果应用中同时存在 `api/_app.ts`,则定义的中间件会被放在 `api/app.ts` 导出实例的最后执行。多数情况下,中间件就能覆盖大多数 BFF 函数的定制需求。只有当应用的服务端逻辑比较复杂时,才需要自定义 Server 实例。
|
152
|
+
:::
|
153
|
+
|
154
|
+
:::caution
|
155
|
+
当应用中没有 `api/app.ts` 的时候,Modern.js 默认会添加 [koa-body](https://www.npmjs.com/package/koa-body)。当应用中存在 `api/app.ts` 时,如果开发者希望使用带有 Body 的 BFF 函数,需要**自行添加** `koa-body`。
|
156
|
+
:::
|
@@ -2,151 +2,85 @@
|
|
2
2
|
sidebar_position: 3
|
3
3
|
title: 运行时框架
|
4
4
|
---
|
5
|
+
|
5
6
|
# 运行时框架
|
6
7
|
|
7
8
|
Modern.js 的 BFF 支持不同的运行时框架,当前 Modern.js 的 BFF 支持两种运行时框架 [Express.js](https://expressjs.com/) 和 [Koa.js](https://koajs.com/)。
|
8
9
|
|
9
|
-
|
10
|
+
使用不同运行时框架时,有部分 API 会存在差异。
|
10
11
|
|
11
|
-
|
12
|
+
## 获取请求上下文
|
12
13
|
|
13
|
-
|
14
|
-
import { hook } from '@modern-js/runtime/server';
|
15
|
-
import { Request, Response, NextFunction } from 'express';
|
14
|
+
在 BFF 函数中,有时需要获取请求上下文,来处理更多逻辑。此时,根据不同的运行时框架,你需要从通过不同的 API 来获取:
|
16
15
|
|
17
|
-
|
18
|
-
addMiddleware(async (req: Request, res: Response, next: NextFunction) => {
|
19
|
-
if (req.url !== '/api/login') {
|
20
|
-
const sid = req?.cookies?.sid;
|
21
|
-
if (!sid) {
|
22
|
-
res.status(400);
|
23
|
-
res.json({ code: -1, message: 'need login' });
|
24
|
-
} else {
|
25
|
-
next();
|
26
|
-
}
|
27
|
-
} else {
|
28
|
-
next();
|
29
|
-
}
|
30
|
-
});
|
31
|
-
});
|
32
|
-
```
|
16
|
+
import { Tabs, Tab as TabItem } from "@theme";
|
33
17
|
|
34
|
-
|
18
|
+
<Tabs>
|
19
|
+
<TabItem value="express" label="Express.js" default>
|
35
20
|
|
36
|
-
```ts
|
37
|
-
|
38
|
-
|
21
|
+
```ts title="api/lambda/hello.ts"
|
22
|
+
import { useContext } from '@modern-js/runtime/express'
|
23
|
+
export const get = async () => {
|
24
|
+
const { req } = useContext();
|
25
|
+
console.info(`access url: ${req.url}`);
|
26
|
+
return 'Hello Modern.js'
|
39
27
|
};
|
40
28
|
```
|
41
29
|
|
42
|
-
|
30
|
+
</TabItem>
|
31
|
+
<TabItem value="koa" label="Koa.js">
|
43
32
|
|
44
|
-
```ts
|
45
|
-
import {
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
useEffect(() => {
|
52
|
-
async function fetchMyApi() {
|
53
|
-
const { message } = await hello();
|
54
|
-
setText(message);
|
55
|
-
}
|
56
|
-
|
57
|
-
fetchMyApi();
|
58
|
-
}, []);
|
59
|
-
|
60
|
-
return <p>{text}</p>;
|
33
|
+
```ts title="api/lambda/hello.ts"
|
34
|
+
import { useContext } from '@modern-js/runtime/koa'
|
35
|
+
export const get = async () => {
|
36
|
+
const ctx = useContext();
|
37
|
+
console.info(`access url: ${req.url}`);
|
38
|
+
return 'Hello Modern.js'
|
61
39
|
};
|
62
40
|
```
|
63
41
|
|
64
|
-
|
42
|
+
</TabItem>
|
43
|
+
</Tabs>
|
65
44
|
|
66
|
-
|
45
|
+
:::info
|
46
|
+
详细内容可以参考 [useContext](/apis/app/runtime/bff/use-context)。
|
47
|
+
:::
|
67
48
|
|
68
|
-
|
49
|
+
## 使用中间件
|
69
50
|
|
70
|
-
|
71
|
-
import { useState, useEffect } from 'react';
|
72
|
-
import { get as hello } from '@api/hello';
|
73
|
-
import { post as login } from '@api/login';
|
51
|
+
在 BFF 函数中,有时需要使用中间件,来处理更多逻辑。此时,根据不同的运行时框架,你需要从通过不同的 API 来获取:
|
74
52
|
|
75
|
-
|
76
|
-
|
53
|
+
<Tabs>
|
54
|
+
<TabItem value="express" label="Express.js" default>
|
77
55
|
|
78
|
-
|
79
|
-
|
80
|
-
const { code } = await login();
|
81
|
-
if (code === 0) {
|
82
|
-
const { message } = await hello();
|
83
|
-
setText(message);
|
84
|
-
}
|
85
|
-
}
|
86
|
-
fetchAfterLogin();
|
87
|
-
}, []);
|
56
|
+
```ts title="api/_app.ts"
|
57
|
+
import { hook } from '@modern-js/runtime/express';
|
88
58
|
|
89
|
-
|
90
|
-
|
59
|
+
export default hook(({ addMiddleware }) => {
|
60
|
+
addMiddleware((req, res, next) => {
|
61
|
+
req.query.id = 'koa';
|
62
|
+
next();
|
63
|
+
});
|
64
|
+
});
|
91
65
|
```
|
92
66
|
|
93
|
-
|
94
|
-
|
95
|
-

|
96
|
-
|
97
|
-
以上代码模拟了在 `/api/_app.ts` 中添加中间件的方式,实现了简易的登录功能。同样,可以在这个钩子文件中实现其他功能来扩展 BFF Server。
|
98
|
-
|
99
|
-
## 框架写法
|
100
|
-
|
101
|
-
框架写法下,Modern.js 不会收集 `api/_app.ts` 中的中间件,运行流程由插件自行控制。
|
102
|
-
|
103
|
-
### Express
|
67
|
+
</TabItem>
|
68
|
+
<TabItem value="koa" label="Koa.js">
|
104
69
|
|
105
|
-
|
70
|
+
```ts title="api/_app.ts"
|
71
|
+
import { hook } from '@modern-js/runtime/koa';
|
106
72
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
const app = express();
|
113
|
-
|
114
|
-
app.put('/user', function (req, res) {
|
115
|
-
res.send('Got a PUT request at /user');
|
116
|
-
});
|
117
|
-
|
118
|
-
app.use(async (req, res, next) => {
|
119
|
-
console.info(`access url: ${req.url}`);
|
120
|
-
next();
|
73
|
+
export default hook(({ addMiddleware }) => {
|
74
|
+
addMiddleware(async (ctx, next) => {
|
75
|
+
ctx.req.query.id = 'koa';
|
76
|
+
await next();
|
77
|
+
});
|
121
78
|
});
|
122
|
-
|
123
|
-
export default app;
|
124
79
|
```
|
125
80
|
|
126
|
-
|
127
|
-
|
128
|
-
Koa 框架写法与 Express 类似,支持在 `app.[tj]s` 定义 API Server 的启动逻辑,执行应用的初始化工作,添加全局中间件,声明路由,扩展原有框架等。
|
129
|
-
|
130
|
-
BFF 函数定义的路由会在 `app.ts` 文件定义的路由之后注册,所以在这里你也可以拦截 BFF 函数定义的路由,进行预处理或是提前响应。
|
131
|
-
|
132
|
-
:::caution 注意
|
133
|
-
在框架写法下,当没有 `app.ts` 的时候,Modern.js 默认会添加 `koa-body`;当有 `app.ts` 时,如果开发者希望使用带有 Body 的 BFF 函数,需要确保 `koa-body` 中间件已经添加。
|
81
|
+
</TabItem>
|
82
|
+
</Tabs>
|
134
83
|
|
84
|
+
:::info
|
85
|
+
详细内容可以参考 [hook](/apis/app/runtime/bff/hook)。
|
135
86
|
:::
|
136
|
-
|
137
|
-
```ts title=api/app.ts
|
138
|
-
import koa from 'koa';
|
139
|
-
|
140
|
-
const app = new Koa();
|
141
|
-
|
142
|
-
app.put('/user', function (req, res) {
|
143
|
-
res.send('Got a PUT request at /user');
|
144
|
-
});
|
145
|
-
|
146
|
-
app.use(async (ctx, next) => {
|
147
|
-
console.info(`access url: ${ctx.url}`);
|
148
|
-
await next();
|
149
|
-
});
|
150
|
-
|
151
|
-
export default app;
|
152
|
-
```
|
@@ -1,10 +1,8 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 1
|
3
|
-
title: 基础用法
|
4
|
-
---
|
5
1
|
# 基础用法
|
6
2
|
|
7
|
-
|
3
|
+
在 Modern.js 应用中,开发者可以在 `api/lambda` 目录下定义接口文件,并导出接口函数。在前端代码中,可以用文件引用的方式,直接调用这些接口函数,发起接口请求。
|
4
|
+
|
5
|
+
这种调用方式我们称为**一体化调用**,开发者无需编写前后端胶水层代码,并天然地保证前后端类型安全。
|
8
6
|
|
9
7
|
## 启用 BFF
|
10
8
|
|
@@ -14,20 +12,15 @@ import EnableBFF from "@site-docs/components/enable-bff"
|
|
14
12
|
|
15
13
|
## BFF 函数
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
:::caution
|
20
|
-
如果是框架模式(有 `api/lambda` 目录),需要创建 `api/lambda/hello.ts`
|
21
|
-
|
22
|
-
:::
|
15
|
+
允许使用一体化调用的函数,我们称为 **BFF 函数**。这里写一个最简单的 BFF 函数,首先创建 `api/lambda/hello.ts` 文件:
|
23
16
|
|
24
|
-
```ts title="api/hello.ts"
|
17
|
+
```ts title="api/lambda/hello.ts"
|
25
18
|
export const get = async () => 'Hello Modern.js';
|
26
19
|
```
|
27
20
|
|
28
|
-
接着在 `src/
|
21
|
+
接着在 `src/routes/page.tsx` 中直接引入函数并调用:
|
29
22
|
|
30
|
-
```tsx title=src/
|
23
|
+
```tsx title="src/routes/page.tsx"
|
31
24
|
import { useState, useEffect } from 'react';
|
32
25
|
import { get as hello } from '@api/hello';
|
33
26
|
|
@@ -41,56 +34,49 @@ export default () => {
|
|
41
34
|
};
|
42
35
|
```
|
43
36
|
|
44
|
-
:::
|
45
|
-
Modern.js
|
37
|
+
:::tip
|
38
|
+
在调用 `new` 命令后,Modern.js 生成器会自动在 `tsconfig.json` 中配置 `@api` 别名,因此可以直接通过别名的方式引入函数。
|
46
39
|
|
47
40
|
:::
|
48
41
|
|
49
|
-
在 `src/
|
42
|
+
在 `src/routes/page.tsx` 中引入的函数,会自动转换成接口调用,不需要再通过请求 SDK 或 Web Fetch 调用接口。
|
50
43
|
|
51
|
-
执行 `pnpm run dev`
|
44
|
+
执行 `pnpm run dev` 后,打开 `http://localhost:8080/` 可以看到页面已经展示了 BFF 函数返回的内容,在 Network 中可以看到页面向 `http://localhost:8080/api/hello` 发送了请求:
|
52
45
|
|
53
46
|

|
54
47
|
|
55
48
|
## 函数路由
|
56
49
|
|
57
|
-
Modern.js 中,BFF
|
58
|
-
|
59
|
-
函数写法下 `api/` 下的所有文件中的每个 BFF 函数都会映射为一个接口。框架写法下 `api/lambda` 下的所有文件中的每个 BFF 函数都会映射为一个接口。
|
60
|
-
|
61
|
-
:::note
|
62
|
-
函数写法和框架写法会在下一节详细介绍。
|
63
|
-
|
64
|
-
:::
|
50
|
+
Modern.js 中,BFF 函数对应的路由系统是基于文件系统实现的,也是一种**约定式路由**。`api/lambda` 下的所有文件中的每个 BFF 函数都会映射为一个接口,下面介绍几种路由的约定。
|
65
51
|
|
52
|
+
:::info
|
66
53
|
所有 BFF 函数生成的路由都带有统一的前缀,默认值为 `/api`。可以通过 [bff.prefix](/configure/app/bff/prefix) 设置公共路由的前缀。
|
67
|
-
|
68
|
-
下面介绍几种路由的约定。
|
54
|
+
:::
|
69
55
|
|
70
56
|
### 默认路由
|
71
57
|
|
72
58
|
以 `index.[jt]s` 命名的文件会被映射到上一层目录。
|
73
59
|
|
74
|
-
- `api/index.ts` -> `{prefix}/`
|
75
|
-
- `api/user/index.ts` -> `{prefix}/user`
|
60
|
+
- `api/lambda/index.ts` -> `{prefix}/`
|
61
|
+
- `api/lambda/user/index.ts` -> `{prefix}/user`
|
76
62
|
|
77
63
|
### 多层路由
|
78
64
|
|
79
65
|
支持解析嵌套的文件,如果创建嵌套文件夹结构,文件仍会以相同方式自动解析路由。
|
80
66
|
|
81
|
-
- `api/hello.ts` -> `{prefix}/hello`
|
82
|
-
- `api/user/list.ts` -> `{prefix}/user/list`
|
67
|
+
- `api/lambda/hello.ts` -> `{prefix}/hello`
|
68
|
+
- `api/lambda/user/list.ts` -> `{prefix}/user/list`
|
83
69
|
|
84
70
|
### 动态路由
|
85
71
|
|
86
|
-
同样的,创建命名带有 `[xxx]` 的文件夹或者文件,支持动态的命名路由参数。动态路由的函数参数规则可以看 [dynamac-path](/guides/advanced-features/bff/function#dynamic-path)
|
72
|
+
同样的,创建命名带有 `[xxx]` 的文件夹或者文件,支持动态的命名路由参数。动态路由的函数参数规则可以看 [dynamac-path](/guides/advanced-features/bff/function#dynamic-path)。
|
87
73
|
|
88
|
-
- `api/user/[username]/info.ts` -> `{prefix}/user/:username/info`
|
89
|
-
- `api/user/username/[action].ts` -> `{prefix}/user/username/:action`
|
74
|
+
- `api/lambda/user/[username]/info.ts` -> `{prefix}/user/:username/info`
|
75
|
+
- `api/lambda/user/username/[action].ts` -> `{prefix}/user/username/:action`
|
90
76
|
|
91
77
|
### 白名单
|
92
78
|
|
93
|
-
默认 `api/` 目录下所有文件都会当作 BFF 函数文件去解析,但以下文件不会被解析:
|
79
|
+
默认 `api/lambda/` 目录下所有文件都会当作 BFF 函数文件去解析,但以下文件不会被解析:
|
94
80
|
|
95
81
|
- 命名以 `_` 开头的文件。例如:`_utils.ts`。
|
96
82
|
- 命名以 `_` 开头的文件夹下所有文件。例如:`_utils/index.ts`、`_utils/cp.ts`。
|
@@ -100,20 +86,18 @@ Modern.js 中,BFF 函数对应的路由系统是基于文件系统实现的,
|
|
100
86
|
|
101
87
|
## RESTful API
|
102
88
|
|
103
|
-
Modern.js 的 BFF 函数需要遵循 RESTful API
|
104
|
-
|
105
|
-
:::info
|
106
|
-
假设函数允许自由定义参数,产出的路由必然由**私有协议**进行调用(原因是无法区分请求参数与请求体),而无法实现任意的 RESTful API。
|
89
|
+
Modern.js 的 BFF 函数需要遵循 RESTful API 标准来定义,开发者需要按照一系列规则来定义 BFF 函数。
|
107
90
|
|
108
|
-
|
91
|
+
:::tip 设计原则
|
92
|
+
BFF 函数不仅会在项目中被调用,也应该允许其他项目通过请求 SDK 或 Web fetch 调用。因此 Modern.js 没有在一体化调用时定义**私有协议**,而是通过标准的 HTTP Method,以及 `params`、`query`、`body` 等通用的 HTTP 请求参数来定义函数。
|
109
93
|
|
110
94
|
:::
|
111
95
|
|
112
|
-
###
|
96
|
+
### 函数导出规则
|
113
97
|
|
114
|
-
|
98
|
+
#### HTTP Method 具名函数
|
115
99
|
|
116
|
-
|
100
|
+
Modern.js BFF 函数的导出名决定了函数对应接口的 HTTP Method,如 `get`,`post` 等。例如导出一个 GET 接口:
|
117
101
|
|
118
102
|
```ts
|
119
103
|
export const get = async () => {
|
@@ -124,7 +108,7 @@ export const get = async () => {
|
|
124
108
|
};
|
125
109
|
```
|
126
110
|
|
127
|
-
按照以下例子,则可导出一个 `POST`
|
111
|
+
按照以下例子,则可导出一个 `POST` 接口:
|
128
112
|
|
129
113
|
```ts
|
130
114
|
export const post = async () => {
|
@@ -139,24 +123,46 @@ export const post = async () => {
|
|
139
123
|
|
140
124
|
- 名字是大小不敏感的,如果是 `GET`,写成 `get`、`Get`、`GEt`、`GET`,都可以准确识别。而默认导出,即 `export default xxx` 则会被映射为 `Get`。
|
141
125
|
|
142
|
-
|
126
|
+
#### 使用 Async 函数
|
143
127
|
|
144
|
-
|
145
|
-
需要注意的是,定义的函数都应该是异步的,与函数调用时类型有关,后面会提到。
|
128
|
+
Modern.js 推荐将 BFF 函数定义为 Async 异步函数,即使函数中不存在异步流程,例如:
|
146
129
|
|
147
|
-
|
130
|
+
```ts
|
131
|
+
export const get = async () => {
|
132
|
+
return {
|
133
|
+
name: 'Modern.js',
|
134
|
+
desc: '现代 web 工程方案',
|
135
|
+
};
|
136
|
+
};
|
137
|
+
```
|
148
138
|
|
149
|
-
|
139
|
+
这是因为在前端调用时,BFF 函数会自动转换成 HTTP 接口调用,而 HTTP 接口调用时异步的,在前端通常会这样使用:
|
140
|
+
|
141
|
+
```tsx title="src/routes/page.tsx"
|
142
|
+
import { useState, useEffect } from 'react';
|
143
|
+
import { get as hello } from '@api/hello';
|
144
|
+
|
145
|
+
export default () => {
|
146
|
+
const [text, setText] = useState('');
|
147
|
+
|
148
|
+
useEffect(() => {
|
149
|
+
hello().then(setText);
|
150
|
+
}, []);
|
151
|
+
return <div>{text}</div>;
|
152
|
+
};
|
153
|
+
```
|
150
154
|
|
151
|
-
|
155
|
+
因此,为了保持类型定义与实际调用体验统一,我们推荐在定义 BFF 函数时将它设置为异步函数。
|
152
156
|
|
153
|
-
|
157
|
+
### 函数参数规则
|
158
|
+
|
159
|
+
函数参数规则分为两块,分别是请求路径中的动态路由(Dynamic Path)和请求选项(RequestOption)。
|
154
160
|
|
155
161
|
#### Dynamic Path
|
156
162
|
|
157
|
-
|
163
|
+
动态路由会作为 BFF 函数第一部分的入参,每个入参对应一段动态路由。例如以下示例,`level` 和 `id` 会作为前两个参数传递到函数中:
|
158
164
|
|
159
|
-
```ts title="api/[level]/[id].ts"
|
165
|
+
```ts title="api/lambda/[level]/[id].ts"
|
160
166
|
export default async (level: number, id: number) => {
|
161
167
|
const userData = await queryUser(level, uid);
|
162
168
|
return userData;
|
@@ -165,7 +171,7 @@ export default async (level: number, id: number) => {
|
|
165
171
|
|
166
172
|
在调用时直接传入动态参数:
|
167
173
|
|
168
|
-
```
|
174
|
+
```tsx title="src/routes/page.tsx"
|
169
175
|
import { useState, useEffect } from 'react';
|
170
176
|
import { get as getUser } from '@api/[level]/[id]';
|
171
177
|
|
@@ -186,7 +192,7 @@ Dynamic Path 之后的参数是包含 querystring、request body 的对象 `Requ
|
|
186
192
|
|
187
193
|
在不存在动态路由的普通函数中,可以从第一个入参中获取传入的 `data` 和 `query`,例如:
|
188
194
|
|
189
|
-
```ts title="api/hello.ts"
|
195
|
+
```ts title="api/lambda/hello.ts"
|
190
196
|
import type { RequestOption } from '@modern-js/runtime/server';
|
191
197
|
|
192
198
|
export async function post({
|
@@ -216,7 +222,7 @@ export async function post({ query, data }: { query: IQuery; data: IData }) {
|
|
216
222
|
|
217
223
|
当函数文件使用动态路由规则时,动态路由会在 `RequestOption` 对象参数前。
|
218
224
|
|
219
|
-
```ts title="api/[sku]/[id]/item.ts"
|
225
|
+
```ts title="api/lambda/[sku]/[id]/item.ts"
|
220
226
|
export async function post(
|
221
227
|
sku: string,
|
222
228
|
id: string,
|
@@ -231,7 +237,7 @@ export async function post(
|
|
231
237
|
|
232
238
|
调用时也按照函数定义,传入对应的参数即可:
|
233
239
|
|
234
|
-
```ts title="
|
240
|
+
```ts title="src/routes/page.tsx"
|
235
241
|
import { post } from '@api/[sku]/[id]/item';
|
236
242
|
|
237
243
|
export default () => {
|
@@ -250,4 +256,8 @@ export default () => {
|
|
250
256
|
};
|
251
257
|
```
|
252
258
|
|
253
|
-
|
259
|
+
## 扩展 BFF 函数
|
260
|
+
|
261
|
+
import ExtendBFF from "@site-docs/components/extend-bff-function"
|
262
|
+
|
263
|
+
<ExtendBFF/>
|