> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 基于 OpenAPI 规范实现 Bookings API

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero
  title="基于 OpenAPI 规范实现 Bookings API"
  description="将 YAML 规范文件交给 Devin，即可生成完整实现的 Express 路由处理器、Prisma 模型、Zod 校验逻辑，以及 Supertest 集成测试——并与现有代码库的模式保持一致。"
  prompt="使用我们现有的 Express + Prisma 技术栈，实现以下 bookings API 端点。按照 src/api/v2/users/ 中的模式来设计路由结构、错误处理和中间件。根据下面规范中的 schema 派生对应的 Zod schema。

Endpoints:
- GET /api/v2/bookings（分页列表：page、startDate、endDate 查询参数）
- POST /api/v2/bookings（创建——成功返回 201，时间段冲突时返回 409）
- GET /api/v2/bookings/:id
- PATCH /api/v2/bookings/:id（部分更新）
- DELETE /api/v2/bookings/:id（软删除）
- POST /api/v2/bookings/:id/confirm（成功返回 200，若已取消则返回 422）
- POST /api/v2/bookings/:id/cancel

CreateBookingInput schema（必填：title、startTime、endTime、roomId）：
- title: string，maxLength 200
- startTime: string，date-time
- endTime: string，date-time
- roomId: string，uuid
- notes: string，maxLength 1000，可选

使用 Supertest 为每个端点编写集成测试——覆盖成功场景、校验错误、认证失败，以及时间段重叠等边界情况。运行所有测试并验证每个端点都正常工作。如果有任何测试失败，进行调试并修复——持续迭代直到所有测试通过。在端到端全都正常之前，不要创建 PR。"
  category="功能开发"
  features=""
/>

<div className="uc-detail-wrapper">
  <Tip>不想手动配置？将本页链接粘贴到 Devin 会话中，让它为你完成所有设置。</Tip>

  <Steps>
    <Step title="（可选）调研你现有的 API 设计模式">
      如果你不确定你的 Express API 是如何构建的，或者不清楚应该参考哪些模式，可以先使用 [Ask Devin](https://app.devin.ai/search?utm_source=docs\&utm_medium=use-case-gallery) 来了解情况：

      <PromptBlock agent="ada">
        ```txt Research our Express API patterns theme={null}
        Show me how our Express API is structured:
        1. What's the folder layout under src/api/v2/?
        2. How do route handlers, controllers, and services interact?
        3. Which middleware do we use for auth and validation?
        4. How are error responses formatted?
        5. What does our test setup look like (Supertest, test DB config)?
        ```
      </PromptBlock>

      你也可以使用 [DeepWiki](https://deepwiki.com) 来探索具有类似模式的开源 API——例如搜索 Express + Prisma + Zod 示例，查看其他项目如何组织路由处理程序和验证逻辑。

      你还可以直接从 Ask Devin 启动 Devin 会话，它会自动继承前面获取的所有上下文信息。
    </Step>

    <Step title="为 Devin 指定你的 OpenAPI 规范">
      首先告诉 Devin 规范文件位于何处，以及要实现哪个资源。Devin 会读取 YAML 中的每个路径、schema 和错误定义，然后与现有的 Express 路由进行交叉比对，自动匹配既有约定。

      下面是 Devin 能处理的这类规范的一个节选——针对 bookings 资源的标准 OpenAPI 3.0 定义：

      ```yaml theme={null}
      # openapi/bookings-v2.yaml (excerpt)
      openapi: "3.0.3"
      info:
        title: Bookings API
        version: "2.0.0"
      paths:
        /api/v2/bookings:
          get:
            summary: List bookings
            parameters:
              - name: page
                in: query
                schema: { type: integer, default: 1 }
              - name: startDate
                in: query
                schema: { type: string, format: date }
              - name: endDate
                in: query
                schema: { type: string, format: date }
            responses:
              "200":
                description: Paginated list of bookings
                content:
                  application/json:
                    schema:
                      $ref: "#/components/schemas/BookingListResponse"
          post:
            summary: Create a booking
            requestBody:
              required: true
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/CreateBookingInput"
            responses:
              "201":
                description: Booking created
              "409":
                description: Time slot conflict
        /api/v2/bookings/{id}/confirm:
          post:
            summary: Confirm a booking
            responses:
              "200":
                description: Booking confirmed
              "422":
                description: Booking already cancelled
      components:
        schemas:
          CreateBookingInput:
            type: object
            required: [title, startTime, endTime, roomId]
            properties:
              title:
                type: string
                maxLength: 200
              startTime:
                type: string
                format: date-time
              endTime:
                type: string
                format: date-time
              roomId:
                type: string
                format: uuid
      ```

      <PromptBlock>
        ```txt 根据 OpenAPI 规范实现 bookings API theme={null}
        为 /api/v2/bookings 实现 openapi/bookings-v2.yaml 中定义的端点。
        该规范定义了：

        - GET /api/v2/bookings（带分页和日期范围过滤的列表）
        - POST /api/v2/bookings（创建）
        - GET /api/v2/bookings/:id
        - PATCH /api/v2/bookings/:id（部分更新）
        - DELETE /api/v2/bookings/:id（软删除）
        - POST /api/v2/bookings/:id/confirm
        - POST /api/v2/bookings/:id/cancel

        使用我们现有的 Express + Prisma 技术栈。参考
        src/api/v2/users/ 中的模式来实现路由结构、错误处理和中间件。
        基于 OpenAPI 中的请求/响应定义推导出 Zod schema。

        ## Testing & verification
        - Write integration tests with Supertest for every endpoint
        - Cover success cases, validation errors, auth failures, and
          business logic edge cases (e.g., overlapping time slots)
        - Run all tests and verify every endpoint works correctly
        - If anything fails — test failures, runtime errors, missing
          middleware — debug and fix it. Keep iterating until all tests pass.
        - Do not open a PR until everything works end-to-end
        ```
      </PromptBlock>

      If your spec isn't checked into the repo yet, paste it directly into the session or attach the YAML/JSON file when starting.
    </Step>

    <Step title="Devin 会匹配你的 Express 模式">
      对你而言，影响最大的一个做法，就是在你的代码库中引用一个实现良好的资源。Devin 会研究那部分代码，并复现其文件夹结构、命名规范、中间件链和错误处理——这样新端点看起来就像是同一位开发者写的。

      例如，Devin 会读取 `src/api/v2/users/router.ts`，然后生成与之匹配的 bookings 路由：

      ```typescript theme={null}
      // src/api/v2/bookings/router.ts  (generated by Devin)
      import { Router } from "express";
      import { authenticate } from "@/middleware/auth";
      import { validate } from "@/middleware/validate";
      import { BookingsController } from "./controller";
      import {
        createBookingSchema,
        updateBookingSchema,
        listBookingsQuerySchema,
      } from "./schemas";

      const router = Router();
      const ctrl = new BookingsController();

      router.use(authenticate);

      router.get("/", validate({ query: listBookingsQuerySchema }), ctrl.list);
      router.post("/", validate({ body: createBookingSchema }), ctrl.create);
      router.get("/:id", ctrl.getById);
      router.patch("/:id", validate({ body: updateBookingSchema }), ctrl.update);
      router.delete("/:id", ctrl.softDelete);
      router.post("/:id/confirm", ctrl.confirm);
      router.post("/:id/cancel", ctrl.cancel);

      export default router;
      ```

      Devin 还会直接根据 OpenAPI 组件定义推导出 Zod schema，因此请求验证会与规范保持同步：

      ```typescript theme={null}
      // src/api/v2/bookings/schemas.ts  (generated by Devin)
      import { z } from "zod";

      export const createBookingSchema = z.object({
        title: z.string().max(200),
        startTime: z.string().datetime(),
        endTime: z.string().datetime(),
        roomId: z.string().uuid(),
        notes: z.string().max(1000).optional(),
      });

      export const updateBookingSchema = createBookingSchema.partial();

      export const listBookingsQuerySchema = z.object({
        page: z.coerce.number().int().positive().default(1),
        limit: z.coerce.number().int().min(1).max(100).default(20),
        startDate: z.string().date().optional(),
        endDate: z.string().date().optional(),
      });
      ```

      请确保你的[环境配置](/zh/onboard-devin/environment)中包含测试数据库配置和所有所需的环境变量，以便 Devin 能够在本地运行完整的测试套件。如果你的 API 需要凭据 (数据库 URL、JWT 密钥等) ，请在开始会话前将其添加为 [Secrets](/zh/product-guides/secrets)，或在会话期间通过聊天提供。
    </Step>

    <Step title="Devin 提交一个经过测试的 PR">
      Devin reads the spec, studies your existing code, and implements each endpoint to match both the OpenAPI contract and your Express codebase conventions. Here's what a typical PR looks like:

      ```
      feat: Implement /api/v2/bookings endpoints from OpenAPI spec

      src/api/v2/bookings/
        router.ts              (Express route definitions + middleware)
        controller.ts          (request handling)
        service.ts             (business logic)
        repository.ts          (Prisma queries)
        schemas.ts             (Zod validation from spec)
      prisma/migrations/
        20260219_add_bookings/  (migration)
      src/__tests__/
        bookings.integration.ts (Supertest tests)
      ```

      Devin 会在创建 PR 之前运行 Supertest 测试套件：

      ```
        /api/v2/bookings
          GET /
            passes returns paginated bookings (42ms)
            passes filters by date range (38ms)
            passes returns 401 without auth (8ms)
          POST /
            passes creates booking with valid data (61ms)
            passes returns 400 for missing required fields (12ms)
            passes returns 409 for overlapping time slot (29ms)
          PATCH /:id
            passes updates booking fields (22ms)
            passes returns 404 for non-existent booking (9ms)
          POST /:id/confirm
            passes transitions status to confirmed (18ms)
            passes returns 422 for already-cancelled booking (11ms)

        16 passing (412ms)
      ```
    </Step>

    <Step title="在规范未涵盖的内容上进行迭代">
      The OpenAPI 规范定义了接口契约，但很少涵盖业务规则、授权逻辑或性能要求。使用后续提示来补充这些空白：

      <PromptBlock>
        ```txt Add authorization rules theme={null}
        只有预订创建者或管理员才应被允许对预订执行 PATCH 或 DELETE。
        confirm 和 cancel 端点也应要求具有 organizer 角色。
        使用我们现有的角色校验中间件 src/middleware/authorize.ts。
        ```
      </PromptBlock>

      <PromptBlock>
        ```txt Add rate limiting and caching theme={null}
        为 POST /api/v2/bookings 添加限流（每位用户每分钟 20 次）。
        将 GET /api/v2/bookings 的响应缓存在 Redis 中，TTL 为 30 秒，
        在任何写操作时使缓存失效。
        ```
      </PromptBlock>
    </Step>

    <Step title="使用 Devin Review 审查此 PR">
      一旦 Devin 创建 PR，使用 [Devin Review](https://app.devin.ai/review?utm_source=docs\&utm_medium=use-case-gallery) 来审查实现。Devin Review 可以发现诸如缺少错误处理、响应格式不一致，或者端点与规范不匹配之类的问题。

      如果 Devin Review 指出了问题，你可以使用 **Autofix** 让 Devin 自动修复这些被标记的问题——它会开启一个后续会话，应用修复，并推送更新后的提交，而你无需手动逐一描述每项更改。
    </Step>
  </Steps>
</div>
