GraphQL API | 在Hasura DDN上引入TypeScript函数

作者:API传播员 · 2025-10-28 · 阅读时间:6分钟

借助 Hasura DDN 对 TypeScript 函数的支持,开发者可以专注于编写返回数据的同步或异步函数,而无需担心复杂的配置。Hasura 会自动进行代码内省,生成生产级 GraphQL API 所需的所有配置,包括 GraphQL 模式、可观察性支持以及高级配置。


为什么选择 Hasura DDN 和 TypeScript 函数?

通过使用简单的 TypeScript 函数,开发者可以快速实现以下功能:

  • 将自定义业务逻辑集成到 Hasura DDN 中。
  • 执行数据转换并与外部 API 集成。
  • 从不受支持的数据源中访问数据。

从头构建 GraphQL 服务器可能面临以下挑战:

1. 编写解析器、类型和数据加载器

手动定义 GraphQL 解析器、类型和数据加载器需要深入了解 GraphQL 模式设计和数据获取策略。这种过程既耗时又容易出错,尤其是在处理复杂数据关系时。

2. 对象关系映射(ORM)

集成 ORM 库可能导致性能瓶颈,并生成低效的 SQL 查询。优化数据库交互需要额外的配置和维护。

3. 访问控制和安全

实现强大的身份验证和授权机制是开发 GraphQL 服务器的关键环节,但也非常复杂。需要遵循安全最佳实践和监管要求。

4. 重复代码的维护成本

手动管理解析器、类型和性能功能会增加开发成本,同时降低开发效率。


Hasura DDN 的 TypeScript 函数优势

Hasura DDN 引入了 TypeScript 函数,简化了 GraphQL 服务器的开发流程。以下是其主要优势:

  • 类型安全:通过 TypeScript 自动推断输入和输出类型。
  • 内联注释:支持在类型定义上添加注释,自动生成 GraphQL 模式描述。
  • 并行执行控制:通过注释指定函数的最大并发执行次数。
  • 高级错误处理:通过抛出不同的错误类型,灵活控制用户可见的错误信息。
  • 性能优化与调试:内置 OpenTelemetry 支持,便于监控和跟踪函数执行。
  • NPM 库支持:可使用任意 NPM 库,简化复杂任务并加速开发。
  • 灵活的模式定义:可指定函数为查询或突变。

示例:创建一个简单的 Hello 函数

以下是一个简单的示例,展示如何创建一个名为 hello 的函数:

/** @readonly */
export function hello(name: string): string {
  return Hello, ${name};
}

通过以下 GraphQL 查询调用该函数:

query MyQuery {
  hello(name: "Sooraj")
}

将函数与数据库字段关联

假设希望将 hello 函数与数据库中的 user 类型关联,可以通过以下方式定义关系:

kind: relationship
version: v1
definition:
  name: helloMessage
  source: user
  target:
    command:
      name: hello
      mapping:
        - source: field
          path:
            - fieldName: name
          target:
            argumentName: name

查询示例:

query MyQuery {
  users {
    name
    helloMessage
  }
}

实际应用:集成 Stripe API

以下示例展示如何使用 Stripe API 查询支付状态:

import Stripe from "stripe";
const stripe = new Stripe("sk_test_XXX");

/**
 * @readonly
 * @paralleldegree 5
 */
export async function getPaymentStatus(paymentId: string): Promise {
  try {
    const paymentIntent = await stripe.paymentIntents.retrieve(paymentId, { expand: ["customer"] });
    return paymentIntent?.status ?? null;
  } catch (error) {
    console.error("Error retrieving payment intent:", error);
    return null;
  }
}

通过以下查询调用该函数:

query MyQuery {
  getPaymentStatus(paymentId: "pi_3P8hrySH1GVlVMsJ1bMaCuMo")
}

将函数与订单表关联

通过以下方式将 getPaymentStatus 函数与订单表关联:

kind: relationship
version: v1
definition:
  name: paymentStatus
  source: orders
  target:
    command:
      name: getPaymentStatus
      mapping:
        - source: field
          path:
            - fieldName: paymentId
          target:
            argumentName: paymentId

查询示例:

query MyQuery {
  sales_orders {
    id
    deliveryDate
    paymentStatus
  }
}

数据转换示例:去除评论中的脏话

以下示例展示如何使用 bad-words NPM 包清理评论文本中的不当语言:

import { Filter } from "bad-words";

/**
 * @readonly
 * @paralleldegree 5
 */
export function removeBadWords(reviewText: string): string {
  const filter = new Filter();
  return filter.clean(reviewText);
}

通过以下方式将函数与评论表关联:

kind: relationship
version: v1
definition:
  name: safeText
  source: reviews
  target:
    command:
      name: removeBadWords
      mapping:
        - source: field
          path:
            - fieldName: text
          target:
            argumentName: text

查询示例:

query MyQuery {
  reviews {
    createdAt
    safeText
  }
}

使用 OpenTelemetry 增强可观察性

Hasura DDN 提供内置的 OpenTelemetry 支持,无需额外配置即可监控和跟踪 Lambda 函数执行。以下是一个示例:

import * as sdk from "@hasura/ndc-lambda-sdk";
import { trace } from "@opentelemetry/api";
import Stripe from "stripe";

const stripe = new Stripe("sk_test_XXX");
const tracer = trace.getTracer("stripeFunction");

/**
 * @readonly
 * @paralleldegree 5
 */
export async function getPaymentStatus(paymentId: string): Promise {
  return await sdk.withActiveSpan(tracer, "Retrieve Stripe Payment Status", async () => {
    try {
      const paymentIntent = await stripe.paymentIntents.retrieve(paymentId, { expand: ["customer"] });
      return paymentIntent?.status ?? null;
    } catch (error) {
      console.error("Error retrieving payment intent:", error);
      return null;
    }
  });
}

错误处理与可见性

Hasura 提供了强大的错误处理机制。以下示例展示如何返回特定的错误信息:

import * as sdk from "@hasura/ndc-lambda-sdk";

/** @readonly */
export function divide(x: number, y: number): number {
  if (y === 0) {
    throw new sdk.UnprocessableContent("Cannot divide by zero", { x, y });
  }
  return x / y;
}

API 响应示例:

{
  "data": null,
  "errors": [
    {
      "message": "ndc: Cannot divide by zero",
      "path": ["divide"],
      "extensions": {
        "details": {
          "x": 10,
          "y": 0
        }
      }
    }
  ]
}

通过 Hasura DDN 的 TypeScript 函数,开发者可以轻松实现复杂的业务逻辑、数据转换以及与外部服务的集成。这种方法不仅简化了开发流程,还提升了系统的可扩展性和性能。

原文链接: https://hasura.io/blog/introducing-typescript-functions-on-hasura-ddn