🧭

OpenAPI において $ref で参照したスキーマに対し参照元で nullable などのキーワードをマージする

2023/03/24に公開

TL;DR

  • $ref と同じレベルに nullable などのキーワードを配置することはできない。
  • $ref でスキーマを共有しつつ、それを利用する側で場合に応じて nullable: true などのキーワードを適用したい場合、 allOf: [...] を利用できる。

例)

    # 参照先の定義内容をそのまま利用する場合:
    hoge:
      $ref: '#/components/schemas/HogeEnum'
    # 参照先の定義内容に `nullable: true` を付け加える場合:
    # ❌ これはできない ($ref と同じレベルにキーワードの指定ができない)
    hoge:
      nullable: true
      $ref: '#/components/schemas/HogeEnum'
    # ✅ これであれば OK $ref で参照したスキーマに nullable: true がマージされる
    hoge:
      nullable: true
      allOf:
        - $ref: '#/components/schemas/HogeEnum'

# 共有されるコンポーネント
components:
  schemas:
    HogeEnum:
      type: string
      description: なんらかのEnum
      enum:
        - hoge
	- fuga
	- piyo

前提

  • OpenAPI 3.0 ベースでの話です

$ref でスキーマを共有する場合の問題点

OpenAPI では $ref を使用することでスキーマの再利用性が向上しますが、柔軟性が低下する場合があります。
それは、$ref と同列に他のキーワードを使用することができないという制約によるものです。
たとえば $refで参照されるスキーマが場合によって制約 ( nullable など ) を加えたい場合です。

これの問題点に対処するため、allOf を活用することが有効です。後続の節では、allOf を使ってこの $ref の制約を克服する方法について具体的に解説します。

allOf を利用して制約を突破する

allOf は、複数のスキーマを組み合わせて新たなスキーマを作成するためのキーワードです。allOf を使うことで、参照されるスキーマのプロパティや制約を継承しつつ、個別に追加の制約やプロパティを設定することが可能です。

allOf を利用したスキーマの合成については以下を参照してください。
Inheritance and Polymorphism

記事冒頭に掲載したコードを抜粋して解説してみます。

たとえば API 定義全体で共有したい以下のスキーマがあるとします。

# 共有されるコンポーネント
components:
  schemas:
    HogeEnum:
      type: string
      description: なんらかのEnum
      enum:
        - hoge
	- fuga
	- piyo

通常、この定義内容をそのまま利用する場合は以下のようになります。

    # 参照先の定義内容をそのまま利用する場合
    hoge:
      $ref: '#/components/schemas/HogeEnum'

しかし、特定のケースでは nullable: true としたいケースがあったりします。 ( たとえば POST では値が必須だけど PATCH/PUT の場合は null 以外を更新対象としたい場合など )

そのような場合は以下のように allOf の配下に $ref を置きます。 allOf には配下の定義を1つにまとめて展開する効果があります。それによって、 allOf の中で参照した $ref の内容と allOf と並列に記述された nullable: true が合成されます。

    # 参照先の定義内容に `nullable: true` を付け加える場合
    hoge:
      nullable: true
      allOf:
        - $ref: '#/components/schemas/HogeEnum'

まとめ

この記事では $ref でスキーマを共有しつつ、特定のケースでは共有したスキーマに手を加たい場合に allOf$ref の制約を回避する方法を示しました。

もし、この記事で示した方法以外により良い方法があるのでしたらコメントなどで教えてもらえると助かります。

参考にしたページなど

Discussion