UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【WebAPI】URI設計 - 単数形か複数形か

WebAPIを設計するときに以下のようなエンドポイントを用意したい

  1. idを指定して、単一のリソースを返す
  2. queryを指定して、単一のリソースを返す
  3. idを複数指定して、複数のリソースを返す

例としては、地図上で表す東京タワーなどのランドマークのリソース(名称、位置情報、説明)にIDを振ったとして

1.はIDで1つのランドマークを検索
2は緯度経度で1つのランドマークを検索
3.は複数のランドマークを検索

というようなことがしたい

ここで迷ったのが、URIは単数形と複数形のどちらにすべきか?

というのもの

Enchant の開発者である Vinay 氏が書いた記事「Best Practices for Designing a Pragmatic RESTful API」 や www.vinaysahni.com

ZalandoのRestfulAPIガイドでは opensource.zalando.com

単数のインスタンスを複数形で表す場合でも「一貫して複数形を使う」とあり、その考え方が広く知られている

しかし、これを忠実に守った場合、2.と3.を共存させることができない

どちらも/landmarks/?someParams=xxxxxというURIになるが、レスポンスが単一のリソースと複数リソースで異なるので

2.を複数に返すようにして、使う側で1要素目だけを使うというようにするのにも違和感がある(緯度経度指定して複数のランドマークが返されるとは思わない)

解決策:単数形のURIを用意してしまう

何の裏付けもないが、エンドポイントの特性上、単数形のURIを用意してしまうのがよいのではないかという結論になった

つまり、先述した2つのAPI設計ガイドとは異なり、単一のリソースを返却する場合は単数形のURIで、複数のリソースを返却する場合は複数形のURIにする

  1. /landmark/13: LandmarkResource
  2. /landmark/?lat=xxxx&lon=yyyy: LandmarkResource
  3. /landmarks/?ids=13,14,15: Array

ガイドから外れたことをするのは怖いが、これで共存とAPIのわかりやすさが解決できたとおもうので、この方針で進めてみる

気付き

もし仮に設計ガイドに従って先に1.と2.を複数形で定義して公開もしてしまっていた場合

3.をあとから追加する際にちょっと面倒なことになっていたと気付いた

2のエンドポイントを変更しなくてはならないか、返却する型を変えなくてはならないから

なので、1つの気づきとしてURIの設計の際にノーシンキングで複数形になっているのは、あとあと変更が面倒なので注意した方がよいかも

【WebAPI】日付のプロパティ値

あるWeb APIの設計をレビューしているときに、日付のプロパティに対して

date: YYYY年MM月DD日
time: hh時mm分

とフォーマットされた文字列で返すと記載されているのに気付いた

フロントエンド目線だとこのようにフォーマットされた状態だと厳しい

例えば、ある画面ではYYYY/MM/DD hh:mmと表示したいかもしれないがそれが簡単にはできなくなる(嫌な予感しかしない)

ということで指摘しようと思ったが、では何に準拠しておくのがよいのかがパッと出てこなかったので調べた

Web API: Good Parts(3.4.3)では

Web API: The Good Parts

Web API: The Good Parts

広く使われることを目的とするならばRFC3339形式がよいとしている

理由は、RFC3339形式は年から秒までを省略せずにすべて含めたもので、かつ、曜日を別表現にするなどの冗長な表現も排除されているため

タイムゾーンに関して、どうやらDBに保存されている日時が日本時間であるためそのまま使うなら末尾に+09:00をつける必要がある

以上から下のフォーマットがよさそう

datetime: YYYY-MM-DDThh:mm:ss+09:00

他参考

https://opensource.zalando.com/restful-api-guidelines/#126

https://wiki.suikawiki.org/n/RFC%203339%E3%81%AE%E6%97%A5%E4%BB%98%E5%BD%A2%E5%BC%8F

is a toyって、なに?

どういう文脈だったか忘れてしまったが、ITエンジニアの会話で「is a toy」という言い回しを聞いた

よく知らなかったので調べた

medium.learningbyshipping.com

上の記事によると、

発明やイノベーションが最初に登場したとき、実用できない「おもちゃ」または「役に立たないもの」と揶揄されるが、徐々にその発明はあたりまえになっていることが多い

そういうのを「is a toy」というみたい

例えば、

  • PC is a toy
  • C programming language is a toy
  • PC Mouse is a toy
  • Mac is a toy
  • Internet is a toy

などなど

今となっては当たり前の技術になっている

これ、「おもちゃじゃんw」って馬鹿にしてたら恥ずかしいやつなので、周りの声に流されず「is a toy」ってやつかもと考えるようにしたい

【Web】App Shell モデルとは

App Shell モデルというワードを知らなかったので調査した

developers.google.com

上のドキュメントより

アプリケーション シェル(App Shell)アーキテクチャは、ネイティブ アプリのように瞬時に、そして確実にユーザーの画面に読み込める Progressive Web App を構築する方法

PWAの一つの方法であることがわかった

主に次のことが特徴といえそう

  • Service Worker をつかって積極的にキャッシュすること
  • オフラインでもユーザー インターフェースが機能するために必要な最小限の HTML、CSSJavaScriptで画面を表示すること
  • ナビゲーションに比較的変更がなく、コンテンツが変更されるアプリやサイトに適していること

Lighthouse を利用すると、App Shell を使用した PWA が高いパフォーマンス基準を満たしているかを検証してくれるみたい

他参考

https://developers.google.com/web/showcase/2016/iowa2016?hl=ja

【CSS】SarariでFlexbox用gapがサポートされた

Safari 14.1からFlexboxでgapが使えるようになったらしい

developer.mozilla.org

f:id:uggds:20210508235809p:plain

.flexbox {
  display: flex;
  gap: 16px;
}

いままでmarginやpaddingを駆使していたので、すごくシンプルになった

【CSS】object-fitで画像をアスペクト比を維持したままコンテンツにおさめる

CLSの対策としてimgタグにwidthとheightを指定したがレスポンシブデザインでブラウザ幅にあわせて画像サイズもかえると縦横比がおかしくなってしまう

調べると以下のスタイルで解決できた

object-fit: contain;

developer.mozilla.org

object-fitは画像をボックスのどこにはめ込むかを指定することができる

IE以外のブラウザなら利用が可能

■ contain

アスペクト比を維持したまま、要素のコンテンツボックスに収まるように拡大縮小される

オブジェクトのアスペクト比とボックスのアスペクト比が合わない場合は、レターボックス表示になる f:id:uggds:20210508230356p:plain:w200

■ cover

アスペクト比を維持したまま、要素のコンテンツボックス全体を埋めるように拡大縮小される

オブジェクトのアスペクト比がボックスのアスペクト比と合わない場合は、オブジェクトの方が合うように切り取られる

f:id:uggds:20210508231653p:plain:w200

■ fill

要素のコンテンツボックス全体を埋めるサイズになる

オブジェクトのアスペクト比がボックスのアスペクト比と合わない場合は、オブジェクトは合うように引き伸ばされる

f:id:uggds:20210508231804p:plain:w200

■ none

拡大縮小されない

imgタグに指定した幅と高さが適用される

f:id:uggds:20210508232224p:plain:w200

■ scale-down

none 又は contain を指定したかのようにサイズが決められ、オブジェクトの実際のサイズが小さいほうを採用する

【Java】実行時間の計測

Javaで処理の実行時間を調べたい

ZonedDateTime.now()でstartとendを算出し、ChronoUnitでその間の時間をだすようにするのがよさそう

https://docs.oracle.com/javase/jp/8/docs/api/java/time/ZonedDateTime.html
https://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/ChronoUnit.html

    ZonedDateTime start = ZonedDateTime.now();
    // 何らかの処理
    ZonedDateTime end = ZonedDateTime.now();
    long spent = ChronoUnit.NANOS.between(start, end);
    System.out.print(TimeUnit.SECONDS.convert(spent, TimeUnit.NANOSECONDS) * 0.001);

これで算出できた