LiferayのREST Builderを使用した独自ヘッドレスAPIの作成方法（パート1）

※この記事は[Liferayコミュニティブログ](https://liferay.dev)に投稿された、_[Creating Headless APIs (Part 1)](https://liferay.dev/blogs/-/blogs/creating-headless-apis-part-1)_を翻訳したものです。

# はじめに

最近私は、Liferayの新しいヘッドレスAPIを用いた開発を試しています。具体的にはヘッドレス・デリバリーとヘッドレス・ユーザー管理モジュールを活用したReactベースのSAPです。4回に渡って実装の内容を紹介していきますが、今回は、REST Builderツールを使用して独自のヘッドレスAPIを作成する方法を紹介します。

ここで次のような疑問を抱くかも知れません。

「_なぜ伝統的なJAX-RSに基づく独自のRESTではなく、REST Builderを使用するのか？_」

LiferayのREST Builderは、単にエンドポイントを公開するアプリケーションをビルドするだけではありません。次の追加機能も提供してくれます。：

- Liferayの認証パイプラインとの統合
- LiferayのCORSの扱いとの統合
- Liferayの検索、フィルタリング、ページングなどとの統合をサポートするためのヘッドレスファシリティ
- 呼び出し側リクエストに対するJSONまたはXMLの生成機能
- 近くリリースされる LiferayのGraphQLエンドポイントとの統合により、REST Builder APIのカスタマイズなしでのGraphQLを経由した利用
- モバイルおよびSPAアプリケーション開発者への一貫性の担保。


これら全部を利用する必要があるわけではなく、適材適所でこれら機能を利用してください。

ブログシリーズの初回を飾る当記事では、新しいREST Builderを使用するためのプロジェクトをセットアップし、エントリポイントを定義するYAMLファイルの主要箇所を紹介します。当記事は長くなるため、いくつかのパートに分けて、まずはサービスパス（エントリポイント）の定義から始め、サービスの構築を説明してきます。

# 事前作業

作業を始める前に、[REST Builderの使い方に関するLiferayの公式ドキュメント](https://portal.liferay.dev/docs/7-2/appdev/-/knowledge_base/a/generating-apis-with-rest-builder)に目を通してください。記事を読むのにはそれほど時間はかかりませんので、読み終えたらまた当記事へ戻ってきてください。

# プロジェクトの開始
次のコマンドを実行して作業用プロジェクトを作成します：

```bash
blade init -v 7.2 vitamins
```

今回はプロジェクトの構成をイメージしやすくするため、プロジェクトを「**ビタミン**」と名付けました。このプロジェクトはビタミンとミネラルで構成され、それらはWebコンテンツよりも多くのビタミンとミネラルを保持している必要があります。そのためヘッドレスREST層（REST Builder）と併せて、カスタムサービス層（Service Builder）が必要になります。

*注：この先、gradle.propertiesでターゲットプラットフォームが有効になっているものとします。すでに設定が済んでいる場合は問題なく次へ読み進めてください。私はターゲットプラットフォームを使用しているため、build.gradleファイル内にバージョンを含めません。*

次にプロジェクトをIDEにロードし、次の手順に従ってheadless-vitamins-implモジュール（後に説明）でbuild.gradleファイルを編集します：https://portal.liferay.dev/docs/7-2/reference/-/knowledge_base/r/rest-builder-gradle-plugin

*注：参照記事に明記されていない注意点として、Liferay Gradle Workspaceを使用している場合、該当ワークスペースが生成するビルドスクリプトは`headress-<名前>-impl`モジュール内の`build.gradle`ファイルに入り、`settings.gradle`ファイルまたはルートレベルの`build.gradle`ファイルには入りません。このケースでビルドスクリプトを`settings.gradle`ファイルへ置こうとすると、`org.gradle.initialization.DefaultSettings_Decorated cannot be cast to org.gradle.api.Project`といった予期せぬエラーが発生します。もしこれが発生した場合は、あなたのREST Builderプラグインが正しくないファイルにリストされていることを意味します。*

`headless-vitamins-impl`モジュール内で`./gradlew tasks`コマンドを実行することで、実際にREST Builderが利用可能であることを確認できます：

```
$ ./gradlew tasks

> Task :tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildCSS - Build CSS files.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildLang - Runs Liferay Lang Builder to translate language property files.
buildNeeded - Assembles and tests this project and all projects it depends on.
buildREST - Runs Liferay REST Builder.
...省略...
```

モジュール一式が必要となるため、ワークスペースの`modules/headless-vitamins`フォルダ（ヘッドレスモジュールを一緒にまとめるためにこのサブディレクトリを作成しました）に`headless-vitamins-api`、`headless-vitamins-impl`、`headless-vitamins-client`、および`headless-vitamins-test`を作成します。参照ドキュメンテーションではこれら追加モジュールの作成には触れていませんが、後に示す通り、これらは必要になってきます。

```
$ cd modules/headless-vitamins
$ blade create -t api -v 7.2 -p com.dnebinger.headless.vitamins headless-vitamins-api
Successfully created project headless-vitamins-api in vitamins/modules/headless-vitamins
$ blade create -t api -v 7.2 -p com.dnebinger.headless.vitamins headless-vitamins-impl
Successfully created project headless-vitamins-impl in vitamins/modules/headless-vitamins
$ blade create -t api -v 7.2 -p com.dnebinger.headless.vitamins headless-vitamins-client
Successfully created project headless-vitamins-client in vitamins/modules/headless-vitamins
$ blade create -t api -v 7.2 -p com.dnebinger.headless.vitamins headless-vitamins-test
Successfully created project headless-vitamins-test in vitamins/modules/headless-vitamins
```


上記コマンドでモジュールを作成する際に、タイプとして`api`を指定したため、いくつかの不要なパッケージやjavaファイルも生成されます。これらをクリーンアップするには、少々時間を要します。また、`headless-vitamins-test`ディレクトリの`src/mai`ディレクトリを `src/testIntegration`へリネームする必要があります。

このプロジェクトはREST Builderがいくつかの統合テストケースを生成しますが、それを機能させるには適切なディレクトリが必要です。 
Liferayのバンドルの標準命名規則に従うため、bnd.bndファイルは`com.dnebinger.headless.vitamins.api`および`com.dnebinger.headless.vitamins.impl`シンボリック名などで更新されます。`build.gradle`ファイルには多くの追加が必要になりますが、もうひと息です。

# サービスの定義

ここから徐々に楽しくなってきます。サービスエンドポイントを定義するためのYAMLファイルを作成しましょう。この作業が初めての場合、気が遠くなると感じるかもしれません。

まずは単純な作業として。`headless-vitamins-impl`内`にrest-config.yaml`ファイルを追加する必要があります。

```yaml
apiDir: "../headless-vitamins-api/src/main/java"
apiPackagePath: "com.dnebinger.headless.vitamins"
application:
    baseURI: "/headless-vitamins"
    className: "HeadlessVitaminsApplication"
    name: "dnebinger.Headless.Vitamins"
author: "Dave Nebinger"
clientDir: "../headless-vitamins-client/src/main/java"
testDir: "../headless-vitamins-test/src/testIntegration/java"
```

これはLiferayのヘッドレスが機能するのに必要な、あなたの作成した要素群を一つに構成したものです。最後の２つのエントリでクライアントとテストを指します。したがって、この作業は先に終わらせます。

次に`rest-openapi.yaml`ファイルを扱います。このファイルも`headless-vitamins-impl`モジュール内に作成されるものです。一度にすべてを捨てるのではなく、ここでは順を追って詳細を理解していきます。すべてのファイル構成は[このリポジトリ](https://github.com/dnebing/vitamins)で確認できます。

各OpenAPI YAMLファイルは**Meta**、**Paths**（エンドポイント）および**Reusable Components**（型定義）の３つのセクションから成り、今回作成したものと違いはありません。

私のメタセクションは次の通りです：

```yaml
openapi: 3.0.1
info:
  title: "Headless Vitamins"
  version: v1.0
  description: "API for accessing Vitamin details."
```

ここまで順調に進んでいます。

*注：単純なテキストエディタはYAMLファイルの編集に向きません。特に、行の折り返しにおける適切なインデント処理に難があります。もし使い慣れたエディタをお持ちでなければ、[https://editor.swagger.io/](https://editor.swagger.io/)で入手可能なSwaggerのアウトラインツールがおすすめです。このツールは適切なインデント処理だけでなく、OpenAPI YAML形式のファイルを扱う際に非常に役立つコード補完機能も有しています。*

# 型の定義

次に、今回作成した再利用可能なコンポーネントを共有します。これらは単体で機能するものではありませんが、後にPathsをカバーする際の負担を軽くします。

私の主要型と「ビタミン」の型：

```yaml
components:
  schemas:
    Vitamin:
      description: Contains all of the data for a single vitamin or mineral.
      properties:
        name:
          description: The vitamin or mineral name.
          type: string
        id:
          description: The vitamin or mineral internal ID.
          type: string
        chemicalNames:
          description: The chemical names of the vitamin or mineral if it has some.
          items:
            type: string
          type: array
        properties:
          description: The chemical properties of the vitamin or mineral if it has some.
          items:
            type: string
          type: array
        group:
          description: The group the vitamin or mineral belongs to, i.e. the B group or A group.
          type: string
        description:
          description: The description of the vitamin or mineral.
          type: string
        articleId:
          description: A journal articleId if there is a web content article for this vitamin.
          type: string
        type:
          description: The type of the vitamin or mineral.
          enum: [Vitamin, Mineral, Other]
          type: string
        attributes:
          description: Health properties attributed to the vitamin or mineral.
          items:
            type: string
          type: array
        risks:
          description: Risks associated with the vitamin or mineral.
          items:
            type: string
          type: array
        symptoms:
          description: Symptoms associated with the vitamin or mineral deficiency.
          items:
            type: string
          type: array
        creator:
          $ref: "#/components/schemas/Creator"
      type: object
```

上記はYAMLの書式です。インデントは階層を表し、より深いインデントの行がその子となり、同階層の行は兄弟を意味します。

「ビタミン」の型は多くのプロパティを持ちます。プロパティは name や id といった単純なものから、より複雑なものまでさまざまです。型プロパティは`String`ですが、可能な値の列挙によって制限されます。クリエイターはこのファイル内における別オブジェクト（`$ref` がこれに当たります）への参照です。

同一ファイル内に`$ref`がある場合、参照を含める必要があることを意味します。Liferayの`headless-delivery`ファイルからコピーした「ビタミン」のクリエイター型は次のとおりです：

```yaml
    Creator:
      description: Represents the user account of the content's creator/author. Properties follow the [creator](https://schema.org/creator) specification.
      properties:
        additionalName:
          description: The author's additional name (e.g., middle name).
          readOnly: true
          type: string
        familyName:
          description: The author's surname.
          readOnly: true
          type: string
        givenName:
          description: The author's first name.
          readOnly: true
          type: string
        id:
          description: The author's ID.
          format: int64
          readOnly: true
          type: integer
        image:
          description: A relative URL to the author's profile image.
          format: uri
          readOnly: true
          type: string
        name:
          description: The author's full name.
          readOnly: true
          type: string
        profileURL:
          description: A relative URL to the author's user profile.
          format: uri
          readOnly: true
          type: string
      type: object
```

型の説明は以上です。

# まとめ

まとめるには早すぎますか？確かにまだ作業は未完了ですね！次のパートはすでに記事にしてあるのでご安心ください。


このパートでは、カスタムヘッドレスサービスと必要なモジュール群を備えた、新しい Liferay Gradle Workspace の作成方法を紹介しました。
また、OpenAPIのYAMLファイルをはじめ、再利用可能なコンポーネントについて理解を深めるために、Pathセクションを飛ばしつつMetaセクションに触れ、「ビタミン」プロジェクトとクリエイターオブジェクトを定義しました。


[次回](https://qiita.com/TsubasaHomma/items/143a843245ff6323fca3)はPaths（エントリポイント）を洗い出していきます
それではまた次のパートでお会いしましょう！

https://github.com/dnebing/vitamins