# @mittwald/api-models 中文文档

规范地址：https://npmcn.com/package/%40mittwald/api-models/

版本：4\.509\.0
npm 元数据更新时间：2026-11-01T19:09:25.544Z
中文译文更新时间：2026-11-01T22:04:13.957Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40mittwald/api-models
原文对照：https://npmcn.com/package/%40mittwald/api-models/?lang=original
项目主页：https://developer.mittwald.de/
源码仓库：https://github.com/mittwald/api-client-js

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：155 kB，191 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：7 个
- peer 依赖：@mittwald/react-use-promise ^2\.6\.0 \|\| ^3\.0\.0 \|\| ^4\.0\.0
- 首次发布：2024-01-12T13:26:29.840Z
- 最新版本发布：2026-11-01T19:09:25.213Z
- 已发布版本数：689（近 12 个月 366 个）

## 中文简介

用于与API进行一致交互的领域模型的集合

## 安装

```sh
npm install @mittwald/api-models
```

也可以使用：`pnpm add @mittwald/api-models`、`yarn add @mittwald/api-models`、`bun add @mittwald/api-models`

## README

# mittwald API模型

这个包包含了一组用于连贯交互的领域模型 使用mittwald API。

## 许可证

版权所有：Mittwald CM Service GmbH & Co. KG

本项目（以及其中的所有NPM软件包）在MIT许可下获得许可； 看到 [许可证](https://npmcn.com/package/LICENSE) 文件查看详细信息。

## 安装

你可以从普通的NPM注册表中安装这个包：

```shell
yarn add @mittwald/api-models
```

## 设置

您将需要初始化API客户端，以便对模型进行操作 由这个包提供。使用 `api` 初始化的全局实例 一些方法。

```typescript
import { api } from "@mittwald/api-models";

api.setupWithApiToken(process.env.MW_API_TOKEN);
```

## 示例

-   一、 **`Reference`** 或者 `ReferenceModel` 代表一个特定的模型 ID。
-   一、 **`DetailedModel`** 包含资源的所有数据。

有关更详细的描述，请参阅本节 [模型类型](https://npmcn.com/package/%40mittwald/api-models/#readme-Type-of-models)

```typescript
// Get a detailed project
const detailedProject = await Project.get("p-vka9t3");

// Create a project reference
const projectRef = Project.ofId("p-vka9t3");

// Get the detailed project from the reference
const anotherDetailedProject = await projectRef.getDetailed();

// Update project description
await detailedProject.updateDescription("My new description!");

// This method just needs the ID and a description and
// thus is also available on the reference
await projectRef.updateDescription("My new description!");

// Accessing the projects server reference
const server = project.server;

// List all projects of this server
const serversProjects = await server.projects.execute();

// List all projects
const allProjects = await Project.query().execute();

// Iterate over project List Models
for (const project of serversProjects) {
  await project.leave();
}
```

## React中的用法

这个包还提供了在React组件中使用的方法。它 使用 [@mittwald / react-use-promise](https://www.npmjs.com/package/@mittwald/react-use-promise) 将所有异步函数封装到AsyncResources中。更多的细节 关于如何使用AsyncResources，请参阅包文档。

### 安装

要使用React客户端，您必须安装附加的 `@mittwald/react-use-promise` 包:

```shell
yarn add @mittwald/react-use-promise
```

所有异步方法都提供 `use`方法属性。这种方法使用 [@mittwald / react-use-promise](https://www.npmjs.com/package/@mittwald/react-use-promise) 在底层以“React的方式”“解析”承诺。

```typescript
const detailedProject = Project.get.use("p-vka9t3");

// Create a project reference
const projectRef = Project.ofId("p-vka9t3");

// Get the detailed project from the reference
const anotherDetailedProject = projectRef.getDetailed.use();

// Accessing the projects server reference
const server = project.server;

// List all projects of this server
const serversProjects = server.projects.execute.use();

// List all projects
const allProjects = Project.query().execute.use();
```

## 不变性和状态更新

这个包提供的大多数模型都代表了一个相关的对应部分 在后端。当从后端加载模型时，当前状态为 合并到模型实例中。为了保持简单和可预测 **状态是不可变的，在任何情况下都不会改变**． 结果是 您必须创建一个新实例来获取更新的模型并传播它 整个运行时代码。

这也适用于在客户端发起的操作。例如，当 `updateDescription` 方法在项目上调用时，项目实例将调用 仍然有旧的描述。

“关注变化”不在本包的范围内，将在 未来版本或其他包™️。

## 贡献

**作为一般的建议，在贡献时，一定要查看现有的源代码 编写代码并将其用作模板！**

在添加或修改模型时，请考虑以下约定。

### 文件结构

在有意义的目录中构建模型。

### 使用基类

模型应该扩展（或继承）正确的基类。你可以找到底 类 `src/base`． 以下是可用的类。

#### `DataModel`

数据模型是所有模型类的基础，这些模型类包含一组 不可变的通用数据。

#### `ReferenceModel`

ReferenceModel仅通过其ID表示某个模型。作为最基本的 模型操作通常只需要ID和一些输入数据(删除、重命名、 …)，参考模型可以避免不必要的API往返。

### 坚持使用通用语言

在添加模型或方法时，请密切关注（可能存在的） 在各自领域中使用的语言。如果是的话，可以和负责的团队谈谈 不确定的。

### 作为抽象层的模型

模型应涵盖以下几个方面：

-   业务逻辑的一致表示
-   简单加载模型及其链接实体
-   以直观的方式与模型交互的方法
-   对模型原始数据进行预处理，增加DX
-   一致的API

### 模型类型

#### 详细模型vs.列表模型

加载单个项目或组件时，某些模型的响应类型通常不同 列表。为了减少数据量，列表响应类型通常是一个子集 的综合模型。为详细模型添加单独的类（命名它） `[Model]Detailed`)和列表模型(命名它 `[Model]ListItem`）。

如果两个模型共享一个公共代码库，您应该添加一个公共模型（命名它） `[Model]Common`）。

#### 参考模型

参考模型仅通过其ID表示某个模型。作为最基本的 模型操作通常只需要ID和一些输入数据(删除、重命名、 …)，参考模型可以避免不必要的API往返。这些类 应该用作新创建模型或链接模型的返回类型。

要得到实际的详细模型，参考模型 _必须_ 有一个 `function getDetailed(): Promise<ModelDetailed>` 和 `function findDetailed(): Promise<ModelDetailed|undefined>` 方法。

在实现入口点模型时考虑扩展参考模型。

#### 查询模型

查询模型通常需要一个查询对象——或者短查询。查询 主要包括分页设置，如 `limit`， `skip` 或者 `page`． 它也可能 包括以下过滤器 `fromDate` 或者 `toDate`，并过滤到其他模型，如 `customerId`.

查询模型表示对特定模型的特定查询，并且应该包括 以下方法：

-   `execute()`：执行查询并返回相应的List Model
-   `refine(overrideQuery)`：创建一个新的查询模型，其中包含一个精细化的查询对象
-   `getTotalCount()`：获取查询的总计数（使用） `limit: 0`）

当一个模型支持查询时，它应该提供一个静态 `query()` 方法 创建各自的查询模型。

当一个模型被用作查询模型中的查询参数时，该模型应该 在该查询模型的参考模型中有一个属性。请参阅以下内容 例子:

```typescript
class Server {
  public readonly projects: ProjectsListQuery;

  public constructor(id: string) {
    this.projects = new ProjectListQuery({
      server: this,
    });
  }
}
```

#### 列表模式

列表模型是查询模型执行的结果。列表模型包括

-   各自列表模型的列表，受分页配置的限制
-   可用的总数（用于实现分页或计数数据）

列表模型应该扩展它们各自的查询模型，因为它可能是 也可以叫 `refine()` 在已执行的查询上。

#### 实现细节

在实现共享功能时，例如在公共模型中，您可以使用 这个 [`polytype`](https://www.npmjs.com/package/polytype) 图书馆实现 动态多重继承。请务必查看现有的源代码 实现的例子。

#### 入口点模型

提供一个模型（命名它） `[Model]`)作为所有不同的入口点 模型类型（详细的、查询的……）。作为惯例，为 这个模型。

### 使用正确的动词

#### `find`

入口点模型应该有一个静态的 `find` 方法。find方法返回 详细的模型或可返回 `undefined` 如果找不到型号。

#### `get`

除了 `find` 方法入口点模型应该有一个静态的 `get` 方法。get方法应该返回所需的对象或抛出一个 `ObjectNotFoundError`． 你可以使用 `find` 方法并断言存在 与 `assertObjectFound` 函数。

#### `query`

当需要查询对象列表时，使用 `query` 方法。它可能支持 `query` 参数以按给定条件过滤结果。

#### `create`

当应该创建模型时，请使用静态 `create` 方法。这种方法应该 返回已创建资源的引用。

### 访问“链接”模型

大多数模型都是更大的模型树的一部分。模型应该提供 属性来获取父模型和子模型，比如 `project.server`， `server.projects` 或者 `server.customer`.

#### 使用参考模型。在可能的情况下使用入口点模型！

如果链接的模型提供了参考模型或入口点模型，则在 模型构造函数，以避免不必要的API往返。

### 模型行为的抽象

模型通常由一组行为支持，这些行为定义了基本模型 交互。为了实际“使用”模型，必须将其初始化为 具体的行为实现。这个抽象层移除 从模型中实现特定的代码，并生成行为 可交换而不会对模型本身产生任何影响-例如在单元内部 测试。

考虑使用以下行为：

-   API交互
-   存储和加载本地数据
-   复杂的计算或逻辑（可能由外部库支持）

#### 将API交互封装在行为中

将任何API交互封装在模型行为中以防止强 模型和api特定实现的耦合。

##### 不要🥴

```typescript
class ProjectDetailed {
  public static async find(
    id: string,
  ): Promise<ProjProjectDetailed | undefined> {
    const response = await client.project.getProject({
      id,
    });

    if (response.status === 200) {
      return new Project(response.data.id, response.data);
    }
    assertStatus(response, 403);
  }
}
```

##### 做😃

```typescript
class ProjectDetailed {
  public static async find(id: string): Promise<ProjectDetailed | undefined> {
    const data = await config.project.behaviors.find(id);
    if (data !== undefined) {
      return new Project(data.id, data);
    }
  }
}
```

#### 如何实现行为

放置一个 `behaviors` 模型内的文件夹，看起来应该是这样的：

```
Project/
├─ behaviors/
│  ├─ index.ts
│  ├─ types.ts (behavior interface)
│  ├─ api.ts   (behavior implementation)
│  ├─ inmem.ts (behavior implementation)

```

##### 定义 `types.ts` 第一个

首先实现行为的接口是一个很好的起点。 接口通常只定义行为中使用的方法。就像

```ts
export interface ProjectBehaviors {
  find: (id: string) => Promise<ProjectData | undefined>;
  updateDescription: (projectId: string, description: string) => Promise<void>;
}
```

然后在全局行为配置中注册该行为 `packages/models/src/config/config.ts`.

##### 使用模型中的行为

如果定义了行为接口，就可以开始实现模型了。你 还可以先实现具体的API行为，来“证明”该行为是什么 “使用”真正的API。

```ts
import { config } from "../../config/config.js";

class ProjectDetailed {
  public static async find(id: string): Promise<ProjectDetailed | undefined> {
    const data = await config.project.behaviors.find(id);
    if (data !== undefined) {
      return new Project(data.id, data);
    }
  }
}
```

##### 实现API行为

API行为依赖于API客户端。您可以将该行为实现为 对象工厂，或实现接口的简单类。在使用 对象工厂，您不必重新声明方法参数类型。

做特定于实现的事情，从而准备和执行请求， 最后处理响应。

```ts
import { ProjectBehaviors } from "./types.js";
import { assertStatus, MittwaldAPIV2Client } from "@mittwald/api-client";

export const apiProjectBehaviors = (
  client: MittwaldAPIV2Client,
): ProjectBehaviors => ({
  find: async (id) => {
    const response = await client.project.getProject({
      projectId: id,
    });

    if (response.status === 200) {
      return response.data;
    }
    assertStatus(response, 403);
  },
});
```

### 为React做准备

所有异步方法都应该提供 `use`方法属性。这个方法 使用 [@mittwald / react-use-promise](https://www.npmjs.com/package/@mittwald/react-use-promise) 在底层以“React的方式”“解析”承诺。

为您提供此功能 _异步_ 建模方法，包装实际方法 与 `provideReact` 增强剂。

```ts
class ProjectDetailed {
  public static find = provideReact(
    async (id: string): Promise<ProjectDetailed | undefined> => {
      const data = await config.behaviors.project.find(id);

      if (data !== undefined) {
        return new ProjectDetailed(data);
      }
    },
  );
}
```

## 常见问题

### @mittwald/api-models 是什么？

用于与API进行一致交互的领域模型的集合

### 如何安装 @mittwald/api-models？

在项目目录运行 npm install @mittwald/api-models，或使用 pnpm add / yarn add / bun add。

### @mittwald/api-models 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @mittwald/api-models 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @mittwald/api-models 可以商用吗？

@mittwald/api-models 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @mittwald/api-models 还在维护吗？

最新版本 4\.509\.0 发布于 2026/11/02（1 天前）；至今共发布 689 个版本，其中过去 12 个月发布了 366 个；首次发布于 2024/01/12。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

