CrescPia

OpenAPI で誤解なく伝える

AI との共同開発のために設計書を書いています。
API 部分については専用の記述方法があるのでそれを利用すると AI にも誤解なく伝わりそう。
専用の記述方法である OpenAPI を利用するようにします。(API の話です。AI ではなく・・・。)


OpenAPI のツールを使うと設定ファイルから API に関するコードを自動生成することが出来ます。
Vibe Coding においては、一部だけでも確定的にコードが生成できると品質が安定するのではないかと思います。


きっかけは、単に AI により正確に伝えるにはということだったのですが、OpenAPI を利用するといろいろと便利なことがありましたので簡単なまとめです。


OpenAPI とは

What Is OpenAPI? | Swagger Docs

OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs. An OpenAPI file allows you to describe your entire API

API specifications can be written in YAML or JSON.

OpenAPI とは REST API の構造を記述するフォーマットです。(以前は Swagger と呼ばれていたようです。)
YAML または JSON フォーマットで記述します。

The format is easy to learn and readable to both humans and machines.

人間にも機械にも読みやすいフォーマットです!
AI に説明するにはもってこいです!

Swagger とは

Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document, and consume REST APIs.

今は、Swagger とは OpenAPI にかかわるツールを表す言葉となっているようです。
これらのツールを使うと API 部分のコードを自動生成できたり、ブラウザで動作させながら仕様を確認できたりします。

YAML ファイル

例えば GET /users/{userId}/posts?limit=20 といった API は以下のように記述します。
パスパラメータは in: path というように書き、クエリーパラメータは in: query というように書きます。

(一部抜粋)
paths:
  /users/{userId}/posts:
    get:
      summary: Get posts by user
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: integer

        - name: limit
          in: query
          description: Number of items to return
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: OK

この定義ファイルを用意するだけで以下の特典が付いてきます。(YAML ファイル自体も AI に書いてもらうのですが。)

  1. フロント側とサーバー側のインターフェースのコードを自動生成できる。
  2. ブラウザで見やすい API 仕様書を作成できる。この仕様書では動作の確認もできる。
  3. AI が API をより正確に理解しやすくなる。

API Documentation & Design Tools for Teams | Swagger

デザインファースト

コードを先に書いておいて、コードから設計書を書くアプローチがあります。
コード上にアノテーションを書いておくとツールを使って設計書(YAMLファイル)を生成できます。
もう一つのアプローチとして、先に設計書を書いておき、これを基にしてコードを生成するというものがあります。
コードを自動生成するというメリットを享受するのであれば、設計書を先に書くアプローチになります。

Best Practices | OpenAPI Documentation


コード自動生成

YAML ファイルからコードを自動生成します。
API が変更になった場合には YAML ファイルを修正してまたコードを自動生成すればよいです。
そうすると、アプリケーション固有の実装部分でエラーになるので修正漏れが防げます。


コード自動生成ツールは、いろいろな言語のものがそろっています。
Code Generators


どんな感じのコードが生成されるのか、以下に抜粋したものを表示します。
僕は、フロント側では Typescript を利用し、API側では Go の Echo を利用しているのでその例になります。

■ フロント側コード生成

openapi-typescript というツールを利用すると以下のようなコードが生成されます。
paths とかデータの型を表す components が定義されています。

/**
 * This file was auto-generated by openapi-typescript.
 * Do not make direct changes to the file.
 */

export interface paths {
    "/projects": {
        parameters: {
            query?: never;
            header?: never;
            path?: never;
            cookie?: never;
        };
        /**
         * List all projects
         * @description Returns a list of projects owned by the authenticated user.
         */
        get: {
            parameters: {
                query?: never;
                header?: never;
                path?: never;
                cookie?: never;
            };
            requestBody?: never;
            responses: {
                /** @description A list of projects */
                200: {
                    headers: {
                        [name: string]: unknown;
                    };
                    content: {
                        "application/json": components["schemas"]["Project"][];
                    };
                };
                /** @description Unauthorized */
                401: {
                    headers: {
                        [name: string]: unknown;
                    };
                    content?: never;
                };
            };
        };
        put?: never;
        /** Create a new project */
        post: {
            parameters: {
                query?: never;
                header?: never;
                path?: never;
                cookie?: never;
            };
            
            ・・・
            (中略)
            ・・・
export type webhooks = Record<string, never>;
export interface components {
    schemas: {
        Project: {
            /**
             * Format: uuid
             * @example 550e8400-e29b-41d4-a716-446655440000
             */
            id: string;
            /** @example My Awesome Project */
            name: string;
            /** @example Project goal is to learn Svelte 5 */
            description?: string;
            /**
             * Format: date
             * @example 2025-01-01
             */
            start_date: string;
            /**
             * Format: date
             * @example 2025-12-31
             */
            end_date: string;
            /**
             * @description Calculated progress percentage based on milestones
             * @example 45
             */
            progress?: number;
            /** Format: date-time */
            created_at?: string;
            /** Format: date-time */
            updated_at?: string;
        };
        ・・・
        (以降省略)

さらに、openapi-fetch という fetch をラップしたライブラリがあります。
paths の定義をそのまま openapi-fetch に渡すと型安全に処理できるようになります。

import createClient from 'openapi-fetch';

export const api = createClient<paths>({
	baseUrl: '/api'
});

const { data, error } = await api.GET('/projects');
■ API側コード生成

oapi-codegen というツールを利用すると以下のようなコードが生成されます。
データ型やインターフェース型が定義されています。
これをベースにインターフェースの実装をしていくことになります。

// Package generated provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.1 DO NOT EDIT.
package generated

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/getkin/kin-openapi/openapi3"
	"github.com/labstack/echo/v4"
	"github.com/oapi-codegen/runtime"
	openapi_types "github.com/oapi-codegen/runtime/types"
)
・・・
中略
・・・
// Project defines model for Project.
type Project struct {
	CreatedAt   *time.Time         `json:"created_at,omitempty"`
	Description *string            `json:"description,omitempty"`
	EndDate     openapi_types.Date `json:"end_date"`
	Id          openapi_types.UUID `json:"id"`
	Name        string             `json:"name"`

	// Progress Calculated progress percentage based on milestones
	Progress  *int               `json:"progress,omitempty"`
	StartDate openapi_types.Date `json:"start_date"`
	UpdatedAt *time.Time         `json:"updated_at,omitempty"`
}

・・・
中略
・・・
// ServerInterface represents all server handlers.
type ServerInterface interface {
	// List all projects
	// (GET /projects)
	GetProjects(ctx echo.Context) error
	// Create a new project
	// (POST /projects)
	PostProjects(ctx echo.Context) error
	// Delete a project
	// (DELETE /projects/{projectId})
	DeleteProjectsProjectId(ctx echo.Context, projectId openapi_types.UUID) error
	// Get a project by ID
	// (GET /projects/{projectId})
	GetProjectsProjectId(ctx echo.Context, projectId openapi_types.UUID) error
	// Update a project
	// (PUT /projects/{projectId})
	PutProjectsProjectId(ctx echo.Context, projectId openapi_types.UUID) error
	// Create a milestone
	// (POST /projects/{projectId}/milestones)
	PostProjectsProjectIdMilestones(ctx echo.Context, projectId openapi_types.UUID) error
	// Delete a milestone
	// (DELETE /projects/{projectId}/milestones/{milestoneId})
	DeleteProjectsProjectIdMilestonesMilestoneId(ctx echo.Context, projectId openapi_types.UUID, milestoneId openapi_types.UUID) error
	// Update a milestone
	// (PUT /projects/{projectId}/milestones/{milestoneId})
	PutProjectsProjectIdMilestonesMilestoneId(ctx echo.Context, projectId openapi_types.UUID, milestoneId openapi_types.UUID) error
}
・・・
以降省略

仕様書

仕様書がブラウザ上で見れるようになります。
この仕様書上では実際にアクセスして動作を見ることができます。
仕様書

まとめ

API に関する仕様は OpenAPI を利用して定義するといろいろな恩恵が得られます。
特にインターフェースにかかわるコードの自動生成が、 AI を利用した開発において品質の安定化に有用と思います。