ハイパーメディアAPI

HAL

BEAR.SundayはHALハイパーメディア(application/hal+json)APIをサポートします。

HALのリソースモデルは以下の要素で構成されます。

  • リンク
  • 埋め込みリソース
  • 状態

HALは従来のリソースの状態のみを表すJSONにリンクの_linksと他リソースを埋め込む_embeddedを加えたものです。HALはAPIを探索可能にしてそのAPIドキュメントをAPI自体から発見することができます。

以下は有効なHALの例です。自身(self)のURIへのリンクを持っています。

{
    "_links": {
        "self": { "href": "/user" }
    }
}

リンクにはrel(relation)があり、どの様な関係でリンクされているかを表ます。HTMLの<link>タグや<a>タグで使われるrelと同様です。

{
    "_links": {
        "next": { "href": "/page=2" }
    }
}

HALについてさらに詳しくはhttp://stateless.co/hal_specification.htmlをご覧ください。

リソースクラス

アノテーションでリンクを貼ったり他のリソースを埋め込むことができます。

リンクが静的なものは@Linkアノテーションで表し、動的なものはbody['_links']に代入します。宣言的に記述できる@Linkアノテーションが良いでしょう。

/**
 * @Link(rel="user", href="/user")
 * @Link(rel="latest-post", href="/latest-post", title="latest post entrty")
 */
public function onGet()

or

public function onGet() {
    // 権限のある場合のみリンクを貼る
    if ($hasCommentPrivilege) {
        $this->body += [
            '_links' => [
                'rel' => 'comment',
                'href' => '/comments/{post-id}',
                'templated' => true
            ]
        ];
    }
}

@Embeded

他のリソースを静的に埋め込むには@Embededアノテーションを使い、動的に埋め込むにはbodyにリクエストを代入します。

/**
 * @Embed(rel="todos", src="/todos{?status}")
 * @Embed(rel="me", src="/me")
 */
public function onGet() : ResourceObject

or

$this->body['_embedded']['todos'] = $this->resource->uri('app://self/todos');

CURIEs

APIの見つけやすさ(API Discoverability)を実現するためにHALCURIEsを使います。

それぞれのAPIのドキュメントへのリンクを貼ったindex.json、またはこのようなリソースクラスをルートに設置します。

<?php

use BEAR\Resource\ResourceObject;

class Index extends ResourceObject
{
    public $body = [
        'message' => 'Welcome to the Polidog.Todo API ! Our hope is to be as self-documenting and RESTful as possible.',
        '_links' => [
            'self' => [
                'href' => '/',
            ],
            'curies' => [
                'name' => 'doc',
                'href' => 'http://apidoc.example.com/rels/{?rel}',
                'templated' => true
            ],
            'doc:todo' => [
                'href' => '/todo/{id}',
                'title' => 'todo item',
                'templated' => true
            ]
        ]
    ];

    public function onGet()
    {
        return $this;
    }
}

_links内でcuriesというドキュメントを定義する特別なトークンを指定します。curiesでは、リソースのドキュメントURIを示すhrefとその名前をnameで指定します。

この例ではtodoリソースに関するドキュメントを取得するためにはhttp://apidoc.example.com/rels/?rel=todo URLにアクセスすれば良いと分かります。

APIドキュメントサービス

Curiesの設置されたAPIサーバーをAPIドキュメントサーバーにもすることができます。APIドキュメントの作成の手間や実際のAPIとのずれやその検証、メンテナンスといった問題を解決します。

サービスするためにはbear/api-docをインストールしてBEAR\ApiDoc\ApiDocページクラスを継承して設置します。

composer require bear/api-doc
<?php
namespace MyVendor\MyPorject\Resource\Page\Rels;

use BEAR\ApiDoc\ApiDoc;

class Index extends ApiDoc
{
}

Json Schemaのフォルダをwebに公開します。

ln -s var/json_schema public/schemas

DocblockコメントとJson Shcemaを使ってAPIドキュメントが自動生成されます。ページクラスは独自のレンダラーを持ち$contextの影響を受けないで、人のためのドキュメント(text/html) をサービスします。$contextの影響を受けないのでAppPageどちらでも設置可能です。

CURIEsがルートに設置されていれば、API自体がハイパーメディアではない生JSONの場合でも利用可能です。リアルタイムに生成されるドキュメントは常にプロパティ情報やバリデーション制約が正確に反映されます。

デモ

git clone https://github.com/koriym/Polidog.Todo.git
cd Polidog.Todo/
composer install
composer setup
composer serve

http://127.0.0.1:8080/rels/でAPI docページが開きます。

ブラウズ可能

HALで記述されたAPIセットはヘッドレスのRESTアプリケーションとして機能します。

WebベースのHAL BrowserやコンソールのCURLコマンドでWebサイトと同じようにルートからリンクを辿って始めて全てのリソースにアクセスできます。

Siren

Sirenハイパーメディア(application/vnd.siren+json)をサポートしたSirenモジュール も利用可能です。