使用 React 和 MooseStack 构建基于 ClickHouse 的 API

图片

本文字数:9751;估计阅读时间:25 分钟

作者:Fiveonefour & ClickHouse Team

本文在公众号【ClickHouseInc】首发

图片

这篇文章是一份实用指南,讲解如何在你的 Web 应用中构建一个基于 ClickHouse 的 API,操作方式对熟悉 Postgres 的开发者来说将非常直观。

你将学会创建一个可用于生产环境的 ClickHouse Cloud 服务,实现从 Postgres 到 ClickHouse 的实时数据同步。

我们将使用 Moose OLAP 为 ClickHouse 表生成原生的 TypeScript 接口,构建完全类型安全的分析 API,并将其集成进应用,最终部署到生产环境。

如果你曾在用户产品中上线分析功能,很可能是基于你现有的事务型(OLTP)数据库,比如 Postgres 或 MongoDB 来构建的。我们都经历过这种做法——这是启动并交付对用户有价值功能最快的方式。但随着用户数量、数据量和查询频率的不断增长,你可能已经开始遇到查询变慢的问题;用户越多、数据越多、查询越频繁,仪表盘就越卡。这不仅影响用户体验,也让你不得不花大量时间调优性能,从而拖慢了新功能的交付节奏。

图片

说明:我们的开源参考应用中的示例仪表盘,采用 Vite + React 的 Web 前端,后端并行使用事务型与分析型数据库

你如果看过一些基准测试,应该已经知道 ClickHouse 是当前最快的分析型(OLAP)数据库。ClickHouse 专为分析场景设计,能在毫秒级完成对数十亿行数据的查询。但将一个新服务接入现有 Web 技术栈,听起来确实有点棘手——你可能对如何集成 Postgres 很熟悉,但不太确定该怎么接入 ClickHouse。

这篇文章正是为此而写的,它将手把手教你如何在 Web 应用中构建基于 ClickHouse 的 API,方式对熟悉 Postgres 的开发者来说也十分友好。同时,它还为未来的团队协作与功能扩展打下良好的开发体验(DX)基础。

我们将使用 ClickHouse 提供极速分析能力,配合 Moose OLAP 带来流畅而熟悉的开发体验:

  • ClickHouse 的极速性能,配合受 ORM 启发的 Schema 代码定义  

  • 从数据库到底层 React Hooks 的端到端类型安全与契约  

  • 面向 Web 应用的本地优先开发方式,实现快速迭代  

  • 支持预览环境和托管迁移的安全部署方案  

图片

整篇文章将通过一个开源参考应用提供配套的代码示例。你可以直接体验它的前端,也可以在 GitHub 上查看完整代码库。

将 Postgres 与 ClickHouse 同步

ClickHouse(以及本文中使用的整个技术栈)都是开源的,可以本地运行,也可以部署在你自己的基础设施上进行自托管。但最快的上手方式是直接在 ClickHouse Cloud 启动一个免费试用集群,只需几分钟就能获得一个可用于生产环境的集群,无需管理任何服务器或集群。稍后你还会看到如何将 ClickHouse Cloud 中的一部分数据同步到本地容器,作为本地 ClickHouse 开发实例的数据源。

准备好 ClickHouse 集群后,第一步是准备数据。

在你的应用中,事务型数据库(如 Postgres)是数据的唯一来源。而 ClickHouse 会通过变更数据捕获(CDC)机制与事务库保持实时同步。ClickHouse Cloud 提供了 ClickPipes——一个简单但高性能的 CDC 引擎,只需不到一分钟就能完成配置。它不仅会将现有数据一次性导入 ClickHouse,还会持续实时同步未来的所有更改。

图片

只需几分钟,你的 Postgres 数据就能完整镜像到一个可用于生产环境的 ClickHouse 集群中。接下来,我们要让这些数据在应用代码中变得可用。

将表结构同步为代码

如果你用过 Drizzle 或 Prisma 等事务型 ORM,应该对 “db pull” 的流程很熟悉——直接从数据库结构生成 TypeScript 类型。Moose OLAP 提供了类似的功能,但它是为 ClickHouse 原生打造的。(大多数事务型 ORM 与 OLAP 数据库不太兼容,因为 ClickHouse 有自己的引擎机制、默认值策略和建模方式,这些在 Postgres 或 MySQL 中都不存在。)

你需要先安装 MooseStack CLI(命令行工具 moose),然后在项目根目录运行命令启动流程:

# initialize a new MooseStack project called "analytics service"
moose init analytics-service --from-remote

按照提示连接远程 ClickHouse 数据库后,MooseStack 会自动探查数据库结构,并生成一个新的 analytics-service 文件夹,包含以下内容:

  • 可复用于 API 和查询的数据模型(TypeScript 类型)  

  • 每张 ClickHouse 表对应的 TypeScript “OlapTable” 对象  

借助 MooseStack OLAP,你可以通过原生 TypeScript 接口与 ClickHouse 表和结构进行交互,例如这样:

/* Link to full source code:
https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/app/externalModels.ts
*/
export interface foo {
    id: Key>;
    name: string;
    description: string | undefined;
    status: string;
    priority: number & ClickHouseInt<"int32">;
    is_active: boolean;
    metadata: string | undefined;
    tags: string[];
    score: string & ClickHouseDecimal<10, 2> | undefined;
    large_text: string | undefined;
    created_at: string & typia.tags.Format<"date-time"> & ClickHousePrecision<6>;
    updated_at: string & typia.tags.Format<"date-time"> & ClickHousePrecision<6>;
    _peerdb_synced_at: string & typia.tags.Format<"date-time"> & ClickHousePrecision<9> & ClickHouseDefault<"now64()">;
    _peerdb_is_deleted: number & ClickHouseInt<"int8">;
    _peerdb_version: number & ClickHouseInt<"int64">;
}

export const FooTable = new OlapTable("foo", {
    orderByFields: ["id"],
    engine: ClickHouseEngines.ReplacingMergeTree,
    ver: "_peerdb_version",
    settings: { index_granularity: "8192" },
    lifeCycle: LifeCycle.EXTERNALLY_MANAGED,
});

至此,最关键的步骤已经完成:ClickHouse 的 Schema 已成功以代码形式呈现。它具备类型定义、版本控制,并与应用的其他代码共存。但代码只是第一步,你还需要实际运行它:写查询、试错、修复和迭代 API,而不影响生产环境。

这正是下一步要做的:将 MooseStack 作为你本地开发环境的一部分来运行。

本地优先的开发体验与初始化数据的 ClickHouse

想想你平时如何做前端开发:你绝不会在本地验证之前就上线一个 UI 改动。

使用 Moose CLI,你可以获得类似的开发体验:你的分析后端作为本地服务与应用并排运行,你可以随时启动服务并用生产数据进行初始化,在不触及 staging 或生产环境的情况下自由尝试和调整。

在我们的参考应用中,这些模块被组织在一个 monorepo 代码仓中(当然你也可以不用 monorepo):

  • /apps/web:基于 Vite + React 的前端应用  

  • /services/transactional:由 Supabase + Drizzle ORM 支持的 Fastify API 服务,提供标准 CRUD 接口  

  • /services/analytical:专为分析而构建的 MooseStack + ClickHouse 后端服务  

因此在本地开发时,只需将这些组件一起启动即可:

cd apps/web && pnpm dev
cd services/transactional && pnpm dev
cd services/analytical && pnpm dev

# or from the root of the monorepo
pnpm dev

如果你按本文操作至今,那么你的本地开发环境现在应包含以下内容:

  • React 前端 → 本地运行  

  • 应用服务器 → 本地运行(访问 Postgres 的 API)  

  • MooseStack 分析服务 → 本地运行(访问 ClickHouse 的 API)

初始化本地 ClickHouse 数据

当你运行 moose dev 命令时,CLI 会自动启动一个本地的 ClickHouse 容器。默认情况下,虽然表结构会被创建,但里面并没有数据。为了能测试查询和 API,我们可以从与你 Postgres 同步的 ClickHouse Cloud 集群中,导入真实的生产数据作为初始化内容。

moose seed clickhouse --connection-string $MOOSE_REMOTE_CLICKHOUSE_URL --limit 100000

示例中会将每张表最多 10 万行数据复制到本地 ClickHouse 实例。这样你就可以完全在本地环境中进行开发,无需访问远程服务,并且操作的数据也具备真实代表性。

进度回顾:目前为止你已经完成了这些重要步骤

  1. 创建了一个具备生产能力的 ClickHouse 服务  

  2. 成功实现了 Postgres 到 ClickHouse 的实时数据同步  

  3. 将数据库结构转化为 TypeScript 类型与契约  

  4. 并在本地的 ClickHouse 开发环境中,加载了这些结构并初始化了数据样本  

此时,分析能力已经不再是“一个只能远程访问的数据库”。它已经融入了你的日常开发流程。你可以在本地运行查询、通过版本控制不断演化数据结构,并像前端或 API 一样享受热更新体验。

构建类型安全的分析 API

从这一刻起,你的 Web 应用不再需要直接访问 ClickHouse。你新增的 analytics-service 可以接收 API 请求,转化为带类型的查询,并自动保障类型契约的正确性。我们现在就来实践,为前端提供带类型约束的 API 端点。

如果你曾用过 OLTP 系统中的 ORM 工具,这一流程会非常熟悉。在 Postgres/MySQL 中,像 Drizzle 或 Prisma 这样的工具允许你用 TypeScript 定义 Schema,生成验证器,并在路由或表单中复用。当 Schema 更新时,验证器也会同步更新,构建时即可发现问题。

在 Moose 中,分析 API 的契约完全由 TypeScript 描述。你定义的请求与响应类型会自动编译为运行时验证器。这样你既能在开发阶段获得类型提示,也能在运行时保障类型安全,无需额外手动编写校验逻辑。

/* Link to full source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/app/apis/foo/consumption/foo-score-over-time-api.ts */

import { Api } from "@514labs/moose-lib";
import { FooTable } from "../../externalModels";

export type FoosScoreOverTimeDataPoint = {
  date: string;
  averageScore: number;
  totalCount: number;
};

export type GetFoosScoreOverTimeParams = { days?: number };

export type GetFoosScoreOverTimeResponse = {
  data: FoosScoreOverTimeDataPoint[];
  queryTime: number;
};

export const scoreOverTimeApi = new Api<
  GetFoosScoreOverTimeParams,
  GetFoosScoreOverTimeResponse
>("foo-score-over-time", async ({ days = 90 }, { client, sql }) => {
  /* scoreOverTimeApi function code, see below */
});

在查询部分,Moose OLAP 让 SQL 具备结构感知能力。OlapTable 对象会暴露出带类型的字段列,因此你在编写查询时可以直接引用它们。这样,SQL 不再是脆弱的字符串拼接,而是由编辑器理解的结构化代码:一旦引用了不存在的字段,IDE 就会立刻提示,而不用等到运行时才发现错误。

/* Link to source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/app/apis/foo/consumption/foo-score-over-time-api.ts */

/* scoreOverTimeApi function code */
const start = new Date();
start.setDate(start.getDate() - days);
const end = new Date();

const startStr = start.toISOString().split("T")[0];
const endStr = end.toISOString().split("T")[0];

const query = sql`
  SELECT
    toDate(${FooTable.columns.created_at}) AS date,
    AVG(${FooTable.columns.score})         AS averageScore,
    COUNT(*)                               AS totalCount
  FROM ${FooTable}
  WHERE toDate(${FooTable.columns.created_at}) BETWEEN toDate(${startStr}) AND toDate(${endStr})
    AND ${FooTable.columns.score} IS NOT NULL
  GROUP BY toDate(${FooTable.columns.created_at})
  ORDER BY date ASC
`;

测试你的 API 端点 

每次运行 moose dev,MooseStack CLI 都会热更新你的 API,并自动生成一个符合 OpenAPI 3.0 标准的规范文件(保存在 .moose/openapi.json 中)。这意味着你可以立即使用 curl、Swagger UI 或 VSCode 插件等工具来测试接口。

curl -X 'GET'
  'http://localhost:4410/api/foo-score-over-time?days=10'
  -H 'accept: application/json'

{
  "data": [
    {
      "date": "2025-06-15",
      "averageScore": 41.81,
      "totalCount": "6"
    },
    {
      "date": "2025-06-16",
      "averageScore": 22.3,
      "totalCount": "5"
    },
    {
      "date": "2025-06-17",
      "averageScore": 52.29,
      "totalCount": "6"
    },
...
  ]
}

每条查询请求也都会实时记录在运行 moose dev 的终端中。如果发生错误,你会同时在 API 响应和开发日志中看到提示。这样的实时反馈机制可以让你快速定位问题并立即修复,而不必等到部署后再去追查。

API Executing API: foo-score-over-time
API Query: SELECT toDate(`created_at`) as date, AVG(`score`) as averageScore, COUNT(*) as totalCount FROM `foo` WHERE toDate(`created_at`) >= toDate('2025-06-15') AND toDate(`created_at`) <= toDate('2025-09-13') AND `score` IS NOT NULL GROUP BY toDate(`created_at`) ORDER BY date ASC
API Query completed: 14ms

在你将旧有低性能的 API 逐步迁移到全新的、极速的 ClickHouse 驱动 API 的过程中,也别忘了善用你常用的 AI 助手。这个快速迭代的开发闭环,对 AI 智能体开发者尤其友好——我们都知道,它们并不总能一次生成完美代码。

打通前后端连接

生成的 OpenAPI 规范不仅仅是文档,它也是前后端集成的桥梁。你无需手动维护 API 契约同步,只需将 OpenAPI 文件交给 Orval、Kubb 等 SDK 生成器即可。

在我们的参考应用中,我们使用 Kubb 从自动生成的 OpenAPI 规范中直接生成一套带完整类型定义的 React fetch 客户端。集成过程非常简单:只需配置 Moose 开发服务器在热更新时触发 SDK 代码生成,并将 Kubb 指向 Moose 输出的 .moose/openapi.yaml 文件即可。

[http_server_config]
on_reload_complete_script="pnpm generate-sdk"

# Link to full source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/moose.config.toml

这样一来,每次 API 或 schema 改动,前端数据请求逻辑就会自动同步更新。在 React 中,只需导入生成的客户端并在组件中调用即可。

[http_server_config]
on_reload_complete_script="pnpm generate-sdk"

# Link to full source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/moose.config.toml

这形成了一个完整的闭环:你的 schema 定义驱动 API,Moose API 自动保障类型契约,前端使用自动生成的客户端始终保持最新状态。整个流程在本地实时运行,随着代码的更改自动响应。你将拥有真正的端到端类型安全体验,无需手动同步类型,也能像调用老旧的 Postgres/MySQL/Mongo 接口一样,轻松集成分析 API。

用 Boreal 上线生产环境

一旦 schema 验证通过、API 本地测试完毕,你就可以准备上线了。由 MooseStack 背后的团队 514 推出的 Boreal,能为分析服务提供如 Vercel 或 Heroku 般的现代开发体验。你只需连接 GitHub 仓库,创建一个分支,Boreal 就会为你生成一个独立的预览环境,并在 ClickHouse 集群中创建对应的 staging 数据库。一旦合并到主分支,MooseStack 服务和 schema 变更便会自动部署上线。

Boreal 的部署不仅包括代码,还会将你最近的 schema 改动转化为数据库迁移操作。为保障上线安全,它会自动将代码逻辑与数据库现状进行比对。如果某些字段因上游流程(如 ClickPipes)被删除,Boreal 会阻止上线,直到你更新代码为止,确保 API 不会指向已删除的字段。你只需更新本地 schema,调整 API,重新测试并再次合并,这次就能确保一切顺利对齐。

moose db pull --connection-string $MOOSE_REMOTE_CLICKHOUSE_URL

MooseStack 本身是开源且支持自托管的。但就像 ClickHouse Cloud 是最快构建生产数据库的方式,Boreal 是构建生产级分析服务的最快通道——内建 CI/CD、预览分支与 GitHub 集成,一应俱全。

快速上手指南 

1. 使用 GitHub 账号登录 boreal.cloud  

2. 链接包含你 MooseStack 项目的 GitHub 仓库  

3. 使用管理员权限连接你的 ClickHouse Cloud 集群  

从这里开始,你的工作流就和本地开发一样简单:打开一个 PR,合并到主分支,其余工作交给 Boreal 完成——包括构建 MooseStack 服务、验证 ClickHouse schema 并部署最新 API。

总结

要为用户提供流畅、响应迅速的分析体验,一套专用的实时分析数据库是必不可少的,而 ClickHouse 是当前最强的解决方案。其实,将 ClickHouse 集成进应用并不复杂。将毫秒级 OLAP 的 ClickHouse 与以本地开发为核心的 MooseStack 搭配使用,既能满足用户对性能的期待,也能为团队带来顺畅的开发体验。

你可以从 ClickHouse Cloud 和 ClickPipes 开始,将事务数据库中的数据镜像过来;用 Moose OLAP 将 schema 同步为代码;用 Moose API 构建带运行时校验的接口;再通过自动生成的 hooks 快速集成到 React 中。当准备上线时,Boreal 能为你提供熟悉且高效的生产发布流程,内含预览环境、数据库迁移与 CI/CD 自动部署。

从现在起,在你的事务型数据库旁无缝引入 ClickHouse,按自己的节奏推进迁移,持续发布功能,再也不用担心用户看到加载中的旋转图标。

征稿启示

面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值