资讯动态

PostHog 前后端双类型生成系统指南:API 类型与查询类型的同步实战

发布时间:2026/9/13 1:45:53 来源:尧图企业网站定制
PostHog 前后端双类型生成系统指南API 类型与查询类型的同步实战【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog本篇技术指南以 PostHog 仓库的 type-system.md 为骨架系统讲解 PostHog 维护前后端类型一致性的两套相互独立的类型生成体系由 Django 序列化器驱动的 Backend → Frontend API 类型链路Orval 生成 TypeScript Zod以及由 TypeScriptschema.ts驱动的 Frontend → Backend 查询类型链路Pydanticschema.py。读完本文你将掌握这两条链路的源码位置、命名规范、重新生成命令、新增产品 API 的标准流程以及validated_request查询参数校验模式的用法。总览两条独立链路不要混为一谈PostHog 通过两套类型生成系统让前端与后端保持同步二者的数据流方向、事实来源与产物完全不同数据流事实来源Source of truth生成产物用途Backend → FrontendDjango 序列化器TypeScript Zod由 Orval 生成API 类型、客户端函数、校验 schemaFrontend → BackendTypeScriptschema.tsPydanticschema.py查询类型HogQL、filters、insights及部分遗留类型这两套系统相互独立切勿混淆。判断一条类型应该走哪条链路核心依据是谁拥有这份数据契约后端序列化器定义 API 响应形状前端 TypeScript 定义请求/查询形状。混淆二者会导致类型漂移与维护混乱。链路一Backend → FrontendAPI 响应类型PostHog 使用 Orval 依据 OpenAPI schema 生成 TypeScript 类型与 API 客户端函数。OpenAPI schema 本身由 Django 序列化器自动推导而来因此序列化器是响应类型的终极事实来源。所有权划分后端拥有响应类型由 Django 序列化器经 OpenAPI 生成前端拥有请求/查询类型查询、过滤器、UI 状态等类型由前端手写维护禁止在前端代码中手动重定义后端响应类型——一旦后端字段变化手写副本就会悄然漂移。类型存放位置类型文件位置可编辑生成的 API 类型api.schemas.tsproducts/product/frontend/generated/否生成的 API 客户端api.tsproducts/product/frontend/generated/否生成的 Zod schemaapi.zod.tsproducts/product/frontend/generated/否核心 API 类型api.schemas.tsfrontend/src/generated/core/否核心 Zod schemaapi.zod.tsfrontend/src/generated/core/否手写类型——frontend/src/types/是永远不要编辑generated/目录下的文件——它们在重新生成时会被整体覆盖。仓库现状与此完全吻合每个产品目录下都存在frontend/generated/api.schemas.ts、api.ts、api.zod.ts三件套例如products/access_control/frontend/generated/、products/actions/frontend/generated/等而核心 API 类型位于 frontend/src/generated/core/api.schemas.ts。以核心类型为例其文件头明确写着/** * Auto-generated from the Django backend OpenAPI schema. * To modify these types, update the Django serializers or views, then run: * hogli build:openapi */这正是改序列化器、跑命令、提交产物工作流的源码级印证。命名约定生成的 schema 类型以Api后缀结尾TaskApi、SurveyApi、DashboardApi操作响应类型遵循 Orval 命名规则tasksListResponse200、tasksCreateResponse201手写类型绝不使用Api后缀。这套约定有效防止生成类型与手写类型之间的命名冲突并让代码审查者一眼识别类型来源见到Api后缀即知它来自后端。Zod 校验 schema每个生成目录还包含api.zod.ts其中的 Zod 校验 schema 同样派生自同一份 OpenAPI 规范。需要特别注意的是只生成Bodyschema——响应 schema、path 参数、query 参数、header 均被排除因为其主要用途是在 API 调用前校验用户输入schema 命名为OperationBody例如import { VisualReviewReposCreateBody } from ../generated/api.zod所有导出都标注/* __PURE__ */使未使用的 schema 能被 tree-shaking 从打包产物中移除。重新生成类型修改了序列化器、ViewSet 或extend_schema装饰器后运行hogli build # 自动检测变更并重建 hogli build:openapi # 或显式运行该 pipelineCI 会在生成类型过期时直接失败因此提交前务必本地重跑并提交重新生成的文件。新增一个产品的 API确保products/your_product/frontend/目录存在将 ViewSet 放在products/your_product/backend/运行hogli build:openapi类型出现在products/your_product/frontend/generated/。位于products/*/backend/的 ViewSet 会根据模块路径自动打上 tag无需手动添加extend_schema(tags[...])。序列化器是响应类型的事实来源尽量使用显式字段类型并在需要处补充help_text以改善 OpenAPI 生成质量。查询参数的文档化与校验validated_request模式对带查询参数的端点PostHog 使用validated_requestWIP 模式from posthog.api.utils import validated_request class MyQuerySerializer(serializers.Serializer): status serializers.ChoiceField(choices[active, archived], requiredFalse) limit serializers.IntegerField(default100, min_value1) validated_request( query_serializerMyQuerySerializer, responses{200: MyResponseSerializer(manyTrue)}, ) action(methods[GET], detailFalse) def my_action(self, request, **kwargs): status request.validated_query_data.get(status) # 使用校验后的数据 ...该模式同时完成两件事校验输入 为 OpenAPI 文档化端点。视图内请使用request.validated_query_data不要手动解析request.query_params。从源码结构看validated_request的实际实现位于 posthog/api/mixins.py其完整签名还支持request_serializer、responses、summary、description、tags、deprecated等参数并具备以下行为细节校验后的请求体会挂载为request.validated_data查询参数挂载为request.validated_query_data默认情况下请求/查询序列化器不带 context 构造与 DRF 自带的get_serializer()不同只有当序列化器的validate()需要读取self.context[request]或self.context[team]例如把被拒请求归因到调用用户/团队时才需传入include_serializer_contextTrue内部通过extend_schema将查询序列化器注入 OpenAPIparameters这正是它能一石二鸟的原因另有strict_request_validation默认True与strict_response_validation默认False两个严格性开关。故障排查类型没有生成确认 ViewSet 位于products/your_product/backend/且products/your_product/frontend/目录存在自动打 tag 依赖模块路径类型形状不对序列化器是事实来源自定义SerializerMethodField的类型需用extend_schema_field显式声明CI 失败本地运行hogli build:openapi并提交重新生成的文件。设计决策为什么提交生成文件让类型变更在 PR 中可见、CI 可捕获漂移、前端构建无需依赖运行中的 Django为什么用Api后缀避免与手写类型冲突见到Api即知来自后端为什么产品间不去重保持产品隔离——修改一个产品的序列化器不会影响另一个产品的类型。链路二Frontend → Backend查询类型像TrendsQuery、FunnelsQuery以及 HogQL filters 这类查询类型在 TypeScript 中定义再生成到 Python 侧。工作流程源frontend/src/queries/schema.tsTypeScript 接口——仓库中实际由 frontend/src/queries/schema/index.ts 作为聚合入口export *若干schema-*.ts子模块schema-general.ts、schema-surveys.ts、schema-assistant-*.ts等文件头注释明确说明本文件编译为schema.json进而成为schema.py让前后端共享类型中间产物frontend/src/queries/schema.jsonJSON Schema仓库中约 5.8 万行输出posthog/schema.pyPydantic 模型仓库中约 3.2 万行。重新生成hogli build # 自动检测变更并重建 hogli build:schema # 或显式运行该 pipeline该命令依次执行两个子步骤build:schema-json——TypeScript → JSON Schemabuild:schema-python——JSON Schema → Pydantic。以生成产物 posthog/schema.py 中的TrendsQuery为例可以看到典型特征kind: Literal[TrendsQuery]判别字段、extraforbid的严格配置、series等必填字段使用带discriminatorkind的联合类型、各字段附带从 TS 注释继承的description与默认值class TrendsQuery(BaseModel): model_config ConfigDict(extraforbid,) aggregation_group_type_index: int | None Field(defaultNone, descriptionGroups aggregation) breakdownFilter: BreakdownFilter | None Field(defaultNone, descriptionBreakdown of the events and actions) dateRange: DateRange | None Field(defaultNone, descriptionDate range for the query) interval: IntervalType | None Field(defaultIntervalType.DAY, descriptionGranularity of the response. Can be one of hour, day, week or month) kind: Literal[TrendsQuery] TrendsQuery series: list[Annotated[EventsNode | ActionsNode | DataWarehouseNode | GroupNode, Field(discriminatorkind)]] Field(..., descriptionEvents and actions to include) ...何时向schema.ts添加类型当需要的类型满足以下条件时加入schema.ts从前端发送到后端作为查询/过滤器需要在后端做校验属于 HogQL 或 insight 定义的一部分。不要添加以下类型仅在前端 UI 中使用的类型仅仅因为 Python 侧需要某个类型——后端专用逻辑应使用手写类型。如果需要在前端使用来自后端的类型请在序列化器中定义走 Backend → Frontend 生成链路如果只是后端专用类型应直接在所属产品内手写 Pydantic 模型例如domain_types.py文件。这一边界在schema/index.ts的注释中也有明文规定该 pipeline 只服务于前端编写的查询类型后端拥有的形状API 响应、后端校验或发出的契约不属于这里应声明在序列化器或 Pydantic 模型中交由 OpenAPI/Orval 链路生成 TypeScript——两条链路的分工在源码层面同样清晰。参与清理从手写类型向生成类型迁移PostHog 正在把手动定义的 API 类型迁移为生成类型并同步清理类型所有权。如果你也想参与可以从以下低门槛任务入手快速上手找出与生成类型重复的手写类型——在frontend/src/types/中搜索Dashboard、Survey、FeatureFlag等接口它们现在往往已有DashboardApi、SurveyApi等生成等价物替换 API 调用返回类型——如果看到api.getManualType(...)的写法改用生成好的函数与类型。触碰既有代码时的原则若某文件为 API 响应导入手写类型考虑迁移到生成类型当 UI 需要的形状与 API 提供的不一致时添加 adapter 函数做适配同一文件内同一实体不要混用手写与生成类型。小结PostHog 的双类型生成体系本质上是所有权的工程化落地后端拥有 API 响应契约Django 序列化器 → OpenAPI → Orval → TS/Zod前端拥有查询契约TSschema.ts→ JSON Schema → Pydantic。维护者只需要遵守三条铁律不编辑generated/产物、按数据流方向选择正确链路、改动事实来源后运行hogli build并提交重新生成的文件即可让前后端在数百个产品的规模下保持类型一致、契约可审计。【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价