返回博客

OSDK TypeScript 前端集成指南

coomia-dip 不仅提供强大的后端数据处理和决策能力,还通过 OSDK(Ontology SDK)为前端应用提供类型安全的 Ontology 访问接口。OSDK TypeScript 版本让前端开发者可以像操作本地对象一样操作 Ontology 中的实体,享受完整的 TypeScript 类型推导和 IDE 自动补全。

Coomia发布于 2026年1月27日7 分钟阅读
分享本文Twitter / X

系列:S12 开发者教程 · 第 17 篇 | 难度:中级 | 阅读时间:15 分钟

OSDK TypeScript 前端集成指南

#引言

coomia-dip 不仅提供强大的后端数据处理和决策能力,还通过 OSDK(Ontology SDK)为前端应用提供类型安全的 Ontology 访问接口。OSDK TypeScript 版本让前端开发者可以像操作本地对象一样操作 Ontology 中的实体,享受完整的 TypeScript 类型推导和 IDE 自动补全。

本教程将带你从代码生成开始,在 React 应用中集成 OSDK,实现对象查询、Action 调用、实时订阅等核心功能。

#1. OSDK 架构概览

#1.1 代码生成流程

OSDK 采用代码生成(Code Generation)方式,从 Ontology Schema 自动生成强类型的 TypeScript 客户端代码:

Code
Ontology Schema (Platform)
    |
    v
osdk-cli generate
    |
    v
Generated TypeScript Code
    |
    +-- types/           # 对象类型定义
    +-- objects/          # 对象操作客户端
    +-- actions/          # Action 调用客户端
    +-- queries/          # OQL 查询客户端
    +-- links/            # 关联关系导航

#1.2 为什么选择代码生成而非运行时

  • 编译时类型安全:属性名拼写错误在编译时就能发现
  • IDE 自动补全:开发体验与操作本地 TypeScript 对象一致
  • Tree-shaking:打包时只包含实际使用的对象类型
  • 零运行时开销:没有反射、没有动态代理

#2. 环境准备

#2.1 安装 OSDK CLI

Bash
npm install -g @coomia-dip/osdk-cli

# 验证
osdk --version

#2.2 生成客户端代码

Bash
# 登录到 coomia-dip 实例
osdk auth login --url http://localhost:8080 --token your-api-token

# 生成 TypeScript 代码
osdk generate typescript \
  --output ./src/generated/ontology \
  --package-name @app/ontology

# 生成的目录结构
src/generated/ontology/
  +-- index.ts
  +-- types/
  |     +-- Order.ts
  |     +-- Customer.ts
  |     +-- Ticket.ts
  +-- objects/
  |     +-- OrderClient.ts
  |     +-- CustomerClient.ts
  +-- actions/
  |     +-- CreateOrderAction.ts
  |     +-- ApproveTicketAction.ts
  +-- client.ts

#2.3 初始化客户端

TypeScript
// src/ontology/client.ts
import { OntoPlatformClient } from '@app/ontology/client';

export const ontologyClient = new OntoPlatformClient({
  baseUrl: import.meta.env.VITE_coomia-dip_URL || 'http://localhost:8080',
  token: import.meta.env.VITE_coomia-dip_TOKEN,
  // 或使用 OAuth2
  auth: {
    type: 'oauth2',
    clientId: import.meta.env.VITE_OAUTH_CLIENT_ID,
    redirectUri: window.location.origin + '/callback',
  },
});

#3. 对象查询

#3.1 基础查询

TypeScript
import { ontologyClient } from './ontology/client';
import { Order, Customer } from '@app/ontology/types';

// 获取单个对象
const order = await ontologyClient.objects.Order.get('order-123');
console.log(order.productName);  // TypeScript 自动补全
console.log(order.totalAmount);   // number 类型

// 列表查询
const orders = await ontologyClient.objects.Order
  .where(o => o.status.eq('ACTIVE'))
  .where(o => o.totalAmount.gt(1000))
  .orderBy('createdAt', 'desc')
  .limit(20)
  .list();

for (const order of orders.data) {
  console.log(`${order.orderId}: ${order.productName} - $${order.totalAmount}`);
}

#3.2 关联导航

TypeScript
// 获取订单关联的客户
const order = await ontologyClient.objects.Order.get('order-123');
const customer = await order.links.orderedBy.get();
console.log(`Customer: ${customer.name}`);

// 获取客户的所有订单
const customer = await ontologyClient.objects.Customer.get('cust-456');
const customerOrders = await customer.links.orders
  .where(o => o.status.eq('COMPLETED'))
  .list();

#3.3 聚合查询

TypeScript
const stats = await ontologyClient.objects.Order
  .where(o => o.createdAt.gte('2025-01-01'))
  .aggregate({
    totalRevenue: agg.sum('totalAmount'),
    avgOrderValue: agg.avg('totalAmount'),
    orderCount: agg.count(),
  })
  .groupBy('status')
  .execute();

for (const group of stats) {
  console.log(`${group.status}: ${group.orderCount} orders, $${group.totalRevenue}`);
}

#4. 在 React 中使用

#4.1 React Query 集成

TypeScript
import { useQuery, useMutation } from '@tanstack/react-query';
import { ontologyClient } from './ontology/client';

function useOrders(status: string) {
  return useQuery({
    queryKey: ['orders', status],
    queryFn: () => ontologyClient.objects.Order
      .where(o => o.status.eq(status))
      .orderBy('createdAt', 'desc')
      .limit(50)
      .list(),
  });
}

function OrderList() {
  const { data, isLoading, error } = useOrders('ACTIVE');

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <table>
      <thead>
        <tr>
          <th>Order ID</th>
          <th>Product</th>
          <th>Amount</th>
          <th>Status</th>
        </tr>
      </thead>
      <tbody>
        {data.data.map(order => (
          <tr key={order.orderId}>
            <td>{order.orderId}</td>
            <td>{order.productName}</td>
            <td>${order.totalAmount.toFixed(2)}</td>
            <td>{order.status}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

#4.2 执行 Action

TypeScript
function useCreateOrder() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (params: {
      customerId: string;
      productName: string;
      quantity: number;
      unitPrice: number;
    }) => ontologyClient.actions.CreateOrder.execute(params),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['orders'] });
    },
  });
}

function CreateOrderForm() {
  const createOrder = useCreateOrder();
  const [form, setForm] = useState({ customerId: '', productName: '', quantity: 1, unitPrice: 0 });

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();
    await createOrder.mutateAsync(form);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={form.productName}
        onChange={e => setForm(f => ({ ...f, productName: e.target.value }))}
        placeholder="Product name" />
      <input type="number" value={form.quantity}
        onChange={e => setForm(f => ({ ...f, quantity: Number(e.target.value) }))} />
      <input type="number" value={form.unitPrice}
        onChange={e => setForm(f => ({ ...f, unitPrice: Number(e.target.value) }))} />
      <button type="submit" disabled={createOrder.isPending}>
        {createOrder.isPending ? 'Creating...' : 'Create Order'}
      </button>
    </form>
  );
}

#4.3 实时订阅

TypeScript
import { useEffect, useState } from 'react';

function useRealtimeOrders() {
  const [orders, setOrders] = useState<Order[]>([]);

  useEffect(() => {
    const subscription = ontologyClient.objects.Order
      .where(o => o.status.eq('ACTIVE'))
      .subscribe({
        onData: (event) => {
          if (event.type === 'CREATED') {
            setOrders(prev => [event.object, ...prev]);
          } else if (event.type === 'UPDATED') {
            setOrders(prev => prev.map(o =>
              o.orderId === event.object.orderId ? event.object : o));
          } else if (event.type === 'DELETED') {
            setOrders(prev => prev.filter(o => o.orderId !== event.object.orderId));
          }
        },
        onError: (err) => console.error('Subscription error:', err),
      });

    return () => subscription.unsubscribe();
  }, []);

  return orders;
}

#5. 高级功能

#5.1 批量操作

TypeScript
const batch = ontologyClient.batch();
batch.objects.Order.update('order-1', { status: 'SHIPPED' });
batch.objects.Order.update('order-2', { status: 'SHIPPED' });
batch.objects.Order.update('order-3', { status: 'SHIPPED' });
const results = await batch.execute();

#5.2 乐观更新

TypeScript
const updateOrder = useMutation({
  mutationFn: ({ orderId, status }) =>
    ontologyClient.objects.Order.update(orderId, { status }),
  onMutate: async ({ orderId, status }) => {
    await queryClient.cancelQueries({ queryKey: ['orders'] });
    const prev = queryClient.getQueryData(['orders']);
    queryClient.setQueryData(['orders'], (old) =>
      old.map(o => o.orderId === orderId ? { ...o, status } : o));
    return { prev };
  },
  onError: (err, vars, context) => {
    queryClient.setQueryData(['orders'], context.prev);
  },
});

#5.3 OQL 自定义查询

TypeScript
const result = await ontologyClient.oql.execute<{
  orderId: string;
  customerName: string;
  totalAmount: number;
}>(`
  SELECT o.orderId, c.name AS customerName, o.totalAmount
  FROM Order o
  JOIN o.orderedBy c
  WHERE o.totalAmount > $1
  ORDER BY o.totalAmount DESC
  LIMIT 10
`, [5000]);

result.rows.forEach(row => {
  console.log(`${row.orderId}: ${row.customerName} - $${row.totalAmount}`);
});

#6. 测试

TypeScript
import { describe, it, expect, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

vi.mock('./ontology/client', () => ({
  ontologyClient: {
    objects: {
      Order: {
        where: vi.fn().mockReturnThis(),
        orderBy: vi.fn().mockReturnThis(),
        limit: vi.fn().mockReturnThis(),
        list: vi.fn().mockResolvedValue({
          data: [
            { orderId: 'O-1', productName: 'Widget', totalAmount: 100, status: 'ACTIVE' },
          ],
        }),
      },
    },
  },
}));

describe('OrderList', () => {
  it('renders orders', async () => {
    const client = new QueryClient();
    render(
      <QueryClientProvider client={client}>
        <OrderList />
      </QueryClientProvider>
    );

    await waitFor(() => {
      expect(screen.getByText('Widget')).toBeInTheDocument();
    });
  });
});

#总结

本教程覆盖了 OSDK TypeScript 在前端应用中的完整使用方法:从代码生成到 React 集成,包括对象查询、关联导航、聚合统计、Action 执行、实时订阅、批量操作和 OQL 自定义查询。OSDK 让前端开发者以类型安全的方式访问 Ontology,大幅提升开发效率和代码质量。

下一篇:[S12-18] 多租户隔离与配置指南 上一篇:[S12-16] Temporal 工作流编排指南