Affamative Way

前向きにグダグダいいながらコード書く

REST API Common Spec としての HTTP Status Code と Error の提案

この記事は Timee Advent Calendar 2023 シリーズ 2 の14日目の記事です。

qiita.com

はじめに

CTO室に10月に入社した id:cos31 です。 色々とやり始めたばかりで、ネタに苦しいので仕事にもタイミーには全く関係のないネタになります!*1

TL;DR

API HTTP Response Code Definition

RFC 9205 - Building Protocols with HTTP (日本語訳) の方針を参考に HTTP Status Codeへのマッピングを無理に行いません。

※ 当該セクションの要約

  • HTTP Status Code は、多くの場合、アプリケーション自体以外でも生成されます、例えばネットワークエラーや、Proxy、CDN、LB、サーバ自体が過負荷になっている場合などです。特定のエラーを受けた際に、クライアントは汎用的なセマンティクスである場合に誤解します。
  • 個々のエラーに HTTP Status Code をアプリケーションエラーを 1対1 マッピングすると既存の HTTP Status Code を利用するなど、多くの悪いプラクティスに繋がります。
  • HTTPを使用するアプリケーションは、エラーを定義して最も適用可能な HTTP Status Code を使用し、疑わしい場合は一般的な HTTP Status Code (200、400、および500)を寛大に使用する必要があります
  • 同じ HTTP Status Codeマッピングされた複数のエラー条件を区別し、上記の誤った分布の問題を回避するために、HTTPを使用したアプリケーションは、応答のメッセージコンテンツおよび/またはヘッダーフィールドにより細かいエラー情報を伝える必要があります。

上記を踏まえて、 HTTP Status CodeAPIの成功または失敗を応答します。

  • 2xx 範囲の Status CodeRequest が正常に処理されたことを示します。
  • 4xx 範囲の Status CodeAPIクライアントが指定した Request 要求が正しくないことを示します。
  • 5xx 範囲の Status CodeAPIサーバーでのエラーを示します。
  • 3xx 範囲の Status Code は 利用しません

2xx

Request がサーバーで正常に処理されたことを示します。

長さがゼロの payload ボディや空の payload を生成してもかまいませんが、200レスポンスは常に payload を持ちます。サーバーが応答に payload を送信したくない場合は、代わりに HTTP ステータス 204(No Content)を利用します。

ステータス 説明
200 OK Request が受け入れられました。応答には結果が含まれています。 GET では、Request されたリソースまたはデータは応答本文内にあります。 PUT または DELETE では、Request は正常に行われ、 payload には結果に関する情報 (新規リソース ID やリソース情報、変更後の情報など) があります。
201 Created この応答コードは PUT または POST の場合に利用します、新規リソースが正常に作成されたことを示しています。 payload には、例えば新規リソースに関する情報や検証情報 (アセットが更新された場合など) が含まれていることがあります。
202 Accepted Request は受け入れられたが、処理が完了していないことを示します。この HTTP Status Code は、実際の操作が本質的に非同期である場合に役立ちます。
204 No Content Request は受け入れられたが、何も戻されなかったことを示します。 これは、Request が処理されても、結果に関する追加情報が戻されなかった場合に戻されます。

Example

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 1234,
  "name": "hoge",
  "category": "fuga",
}

4xx

クライアントの Request 要求での不正時に利用します。これは、パラメータの指定や、パラメータに指定されたリソースに対する状態の不備時(指定されたリソースが存在しない、指定されたステータスへの変更ができない場合など)が含まれます

ステータス 説明
400 Bad Request Request が無効な場合に利用します。 このコードは、サーバーが処理しようとしたが、要求の一部が無効であった (例えば、不正なRequest 構文、無効なRequest メッセージパラメータ、指定のリソース自体が存在しない) 場合に戻されます。 Request のエラーに関する情報は、応答本文に記載されており、type と title を含んでいます。
401 Unauthorized 認証に失敗した場合に、サーバが応答します。
403 Forbidden アクセス権限のないリソースにアクセスしようとしたことを示します。具体的には、access token に対しての scope が不足している場合などに用いられます
404 Not Found 指定された path に対する処理が存在しないことを示します。指定されたリソースが存在しない場合には 400 を利用してください
405 Method Not Allowed Request された HTTP メソッドをサポートしていない場合に応答します。
406 Not Acceptable Accept Request ヘッダ に application/json 以外が要求さた場合に応答します。
415 Unsupported Media Type Content-Type が Request ヘッダで示されるクライアントが提供したメディアタイプを、APIが処理できないことを示します。

5xx

サーバ側での問題を示します。

ステータス 説明
500 Internal Server Error サーバーで内部エラーが発生した場合に応答します。 エラー情報は応答本文にあります。
503 Service Unavailable サーバーがメンテナンス中のために停止していることや、過負荷状態になっている場合に応答します。依存する外部サービスや、インフラ基盤側での問題時に利用されます

Errors

Problem Details

4xx 範囲のプログラムで処理可能なエラーについては、RFC 9457 Problem Details for HTTP APIs (日本語訳) に従って詳細なエラー情報を含みます。

エラー応答時には Content-Type: application/problem+json としてエラー詳細を payload に記載し返却します

Problem Detail Object (bia: RFC 9457 #Section 3.1 区分 より以下のMemberを採用して利用します。 *2

member type condition detail
type string required 問題のタイプを識別するURI参照。
title string required 人間が読める形式の問題タイプの概要。ローカライゼーションの目的を除いて、問題の発生ごとに変更するべきではありません
detail string この問題の発生に固有の、人間が読める説明

また、Memberを拡張として、追加する場合には type毎に変動がないようにAPI毎に定義します。

type Format

https://example.com{/path}

pathは 半角小文字英数字 + _ ( [0-9a-z\_]+ ) とします。

# Good
https://example.com/validation_error

# Bad
https://example.com/validation-error

Common types

type path title usage additonal member
/request_unauthorized API key authentication failed 認証失敗した場合 -
/json_invalid Your request json invalid JSON形式が不正の場合 -
/validation_error Your request parameters didn't validate OpenAPIなどの data schema としての validation error の場合や、DB データとの整合性を検証した validation error の場合 invalid_params: [] ※失敗したパラメータ箇所を列挙
/resource_not_found Specified resource does not exist 指定したResourceが存在しない場合(※ コンテキストに応じて論理削除である状態も含む) detail: 存在しなかった対象を明示
/resource_already_exist Specified resource already exist 重複して指定したResourceを作成しようとした場合 detail: 失敗対象の明示
/mutation_error Mutation failed DBへの変更操作(e.g. create, update, delete)に失敗した場合 detail: 失敗対象の明示
/server_error Something wrong occurs サーバー側での不測エラーが発生した場合(※ 500 Internal Server Error への対応を想定) -
/user_unavailable Specified user unavailable 指定したユーザーが処理を継続できない状態(e.g. ステータスが無効な状態) の場合 detail: 何故できないかの詳細を記載します

Example

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
    "type": "https://example.com/unauthorized",
    "title": "API Key Authenticate failed"
}

detail が入るパターン

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
    "type": "https://example.com/resource_not_found",
    "title": "Specified resource does not exist",
    "detail": "User ID: 1234 not exists"
}

• Memberを拡張するパターンのSample

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
  "type": "https://example.net/validation_error",
  "title": "Your request parameters didn't validate.",
  "invalid_params": [
    {
       "name": "age",
       "reason": "must be a positive integer"
    },
    {
       "name": "color",
       "reason": "must be 'green', 'red' or 'blue'"
    }
  ]
}

まとめ

これくらい迷わない定義があると共通理解として整理もできて、クライアントとしても監視観点としても整理が明確になると思っています

しかしながら、めんどくさい部分としては、Problem Detailtype に 応じた Collection の増減の型定義ですが、microservice などでドメインが揃うAPI郡などでまとまると見通しが良い範囲に収まると思います

実例としては、Stripe では、type 指定された URI に対してドキュメントが準備されており、大変クライアントフレンドリーとなっています stripe.com

他、APIとしての共通的な振る舞いに悩んだ場合には、 https://google.aip.dev/ を見ましょう。とても良いです。

特に Pagination とか独自提示したくもないけど標準仕様もない悩ましい Collection Resource への定義は秀逸なので是非見てください

google.aip.dev

このように自身で考えるよりも、標準仕様や先人たちの考えて成果にうまく乗っかりながら、程よいアレンジをして共通仕様として育てると治安の良いAPIになると思います!

We’re Hiring!

タイミーでは、ともに働くメンバーを募集しています!!

現在募集中のポジションはこちらです! hrmos.co

「より深く話を聞きたい」とか「で、お前今何やってんの?」と思われた方は、是非一度カジュアル面談でお話ししましょう!

product-recruit.timee.co.jp

*1:前職でもアーキテクトやってきたんですが、しばらくはやらなそうなので忘れるためのまとめ

*2:status や instance も改定時に追加されましたが、ユースケースの場合分けが難しいのと必要時に個別に検討することを期待しての、まずは最低限として定義

Kinesis Advantage360 Professional が届いた

www.ergonomics.co.jp

ようやく日本でも買える用になりましたね! 販売開始から 1日ともたなかったようですが、在庫復活が期待されます

$459 が、税込 75,768 円とは円安恐ろしい。。

このTweetみて、日本の代理店の日本エルゴノミクス社(昔からkinesisの代理店をやってるとこ)に失礼承知で問い合わせところ、Proは予定ないけど、通常版は年内に売るよ!とお返事いただいており諦めていた。。。

が、販売開始と同時にメールでProも販売開始したよ!と連絡いただけて価格に一瞬躊躇しましたが、「次回入荷は未定だからな!」と念押しがあったので心も無にして購入しました

ここがすごいぞ、Professional

kinensis 分離型でワイヤレスになった!というだけでもう完璧なんですが更なる加点要素として

完全分離での 個別でのBluetooth 接続

よくある分離型のキーボード同士をつなぐ線もない

角度が3段階に角度が変えられる

via: https://kinesis-ergo.com/shop/adv360pro/

一番高い角度が思ったよりも快適で素敵

バックライトLED付き!

嬉しい人は多いのではないでしょうか!

keyremap がキーボード完結以外に GUI だったり生コード書ける

ZMK がファームに採用されおり、自作キーボード勢にも馴染みのある設定が可能です 無印は、ソフトウェア提供されてremap等ができるんですが、Pro用はなんと github で公開されてる repos を fork して github action で build して、自分でファームの入れ替えできるという提供方法になります

実際のkeyremapの手順

1. 自分用のrepos作成

github.com

を forkして、自分のrepos側で、action を有効化する

2. remap

コードを直接いじってもいいんですが、webベースのGUIが提供されてるのでそちらでやりましょう

polarityworks.github.io

github OAuthして、integration として追加したら repos と branch を選択して、自分の好みのキー配置に変更可能です

私の暫定版

Commit Change すると、github action がファームの build を開始します

3. ファームのインストール

完了した build から Artifacts をダウンロードします

Artifacts

私の暫定版のファーム

github.com

PCとType-Cケーブルで左右で順番にPCにケーブルでつないでファームをインストールします

以下の位置の bootloader モードへの切り替えボタンを DOUBLE-CLICK します。

bootloader のスイッチ

※ この時、キーストロークすると bootloader はキャンセルされますので、注意してください

Finder などで、Drive として認識されたキーボードに Buildされた中身の「left.uf2, right.uf2」 をコピーすると、勝手にインストールが開始されて完了次第に Unmount されファームのインストールは完了です

これを左右とも実施すればOKです

追記 (2022-11-04)

キーボードが反応せずに左右の赤いランプが点滅しちゃう時

左右で接続に失敗してる場合、赤いランプが出てない方の本体 Swith On/Off すると解消する 何回やってもダメな時は慌てずに、もう片方をやると解消する

充電の持ち加減

操作しない場合は、勝手に Sleep することもあって、最初に充電して依頼まだ充電してない 2週間程度は持ちそうな気配

充電方法

Type-C で給電します 注意点としては、利用PC本体と接続しないとUSB接続モードとなるようで、AC電源からの給電する場合はキーボード利用しない場合にやると良さそう 隔週くらいで、寝る前に充電しておけば十分な体感です

使用感

茶軸からの茶軸乗り換えなのに、元のよりもタイプがいい感じです 角度がつけられてるのと、より広げられる恩恵は大きい

ただーし、Esc がなくなってる影響が地味にでかく、まだ慣れなれてません。。

参考リンク集

日本代理店

www.ergonomics.co.jp

本家

kinesis-ergo.com

Remap Editor

kinesiscorporation.github.io

ベルフェイスのIDPを支えるアーキテクチャ

はじめに

この記事は🎄bellFace Advent Calendar 2021🎄の15日目の内容です。

adventar.org

自己紹介

今年の5月にベルフェイス株式会社にCTO室長としてJOINしてました id:cos31 こと北上です

bs.bell-face.com

外向けの文章なんて、ウン年ぶり*1のせっかくの機会なので、みんなが Notionづいている中で、今後の継続を込めてここで書きます!

ブログの空白期間としては、KLab → DeNAリクルート → ベルフェイス と各所で頑張っておりました

CTO室としての取り組みは @zigorou さんの一年の振り返りを見てもらうとして zigorou.super.site

現在取り組んでいる ID基盤刷新にまつわるアーキテクチャをご紹介させていただきます

ID基盤とは

詳細は省き、簡単にいうとこんな役割です

  • 安全に複数サービスに対しての横断的な認証・認可の仕組みを提供する
  • ユーザ、クライアント毎に権限設定を一元管理する
  • システム間の責務分担を明確にし、適切で必要な権限を用いて利用可能とする

今回は要件的に、IDaaS を利用せず OpenID ConnectOpenID Provider を自社で作ることを選択しました

既存の認証の必要だったシステムを Relying Party として利用する形式をとります

システムアーキテクチャ

いわゆる、BFF アーキテクチャを採用し、Backend API を呼び出す形です

f:id:cos31:20211214233253p:plain
Component Architecture

現フェーズではコンポーネント間をまたいだDBへの参照及び、Queueへのジョブ追加を許容し、書き込みに関しては各コンポーネントに閉じて実施しています

Identity API は、OpenID Connect Session Management として、 ステートレスなAPIではなく認証状態の管理を行います*2

Frontend

Express Custom Server の一部を Next.js を SSR*3利用し、BFF*4 として構築しています

f:id:cos31:20211214232423p:plain

また、OIDC*5特有の 一部のエンドポイントではUIなしでの処理をするフローも存在しています

frontendの技術顧問として参画いただいている @quramy に実装基盤を作成していただきました

採用ライブラリ

Backend

レイヤードアーキテクチャによる go な APIサーバー を採用しました

Layered Architecture

レイヤーで責務を分けることで、テストを容易にし、かつそれぞれのパッケージや構造体をシンプルに、依存の方向もシンプルかつ最小限となることを期待して採用しています

採用ライブラリ

goの技術顧問として参画いただいている @gami に実装基盤を作成していただきました

現在は別の進化を遂げていますが、ベースとなったものは公開されていますので、詳細はご参考ください

github.com

Frontend と Backend の並走開発

Frontend と Backend にて技術スタック別にメンバーを分担して開発を進めていました 接合点として、Frontend と API を設計をした Design Doc*6*7 にて合意します

DesginDocシーケンスのイメージ

Design Doc の API のふるまいをベースに、個別でスキーマ定義を行い、両チームがブロッカーとならないようにしています

  • Backend は OpenAPI 形式のコード生成
  • Frontend は agreed による Type定義 及び スタブサーバーの起動

スキーマ定義は、お互いの関心が、違うため自由度を持つためにあえて個別化しています

  • Backend では、IF定義と実データの振る舞いが重要
    • ポータブルでコード生成しやすい定義ファイルがほしい
  • Frontend では、API 応答のバリエーションを持つスタブと型定義
    • DBには興味はないので、柔軟なエラーやレスポンスのバリエーションを持ったスタブサーバがほしい

初期開発は自身の領域に集中して取り組み、Frontend と Backend 結合期間にて、細かい調整を行いました

なお、別のスキーマ定義があることが多重管理されることの懸念となりますが、agreed は Open API 定義を出力可能できるので、今後のフローとしては、Desgin Doc → agreed → Open API となります

まとめ

Open ID Connect 実装という、なかなかこってりした仕組みのため、UI と 認証のビジネスロジックをわける過程の整理・設計は大変でした。。

しかし、その甲斐あって、分担して開発していくフェーズでは、お互いに知らなくていことは把握せずとも、個別の開発進行ができました!*8

WE ARE HIRING!

OpenID Connectや、go な API & Next.js で 開発しながら爆発的成長したいエンジニアの皆様 hrmos.co

顧客が本当に欲しかったものを考えることが好きなプロダクトマネージャ、営業領域におけるDXを推進してみたい営業など、様々な職種で募集しております! hrmos.co

まずは、カジュアルに話すところから始めませんか?*9 meety.net

Who's NEXT

デザインチームの皆様での、リバースモデリングの話 です

個人的にも非常に楽しみにしてます!

adventar.org

*1:まともな記事としては10年ぶりという。。

*2:RP-Initiated Logoutなどを実装するので、Session Management が必要 https://openid.net/specs/openid-connect-rpinitiated-1_0.html

*3:Server Side Rendering

*4:Browser for Frontend

*5:Open ID Connectの略称

*6:Design Docs at Google https://www.industrialempathy.com/posts/design-docs-at-google/

*7:上級エンジニアになるための Design Doc 超入門 https://www.atmarkit.co.jp/ait/articles/1606/21/news016.html

*8:もちろん間に立って両方を把握する人もいます

*9:と、いいつつ僕がいないのでご用命あれば、twitterで気軽に呼んでください

前向きにグダグダいいながらコード書く

はてダから移行してやった!

マークアップ記法変わったの試しに書いてみよーっと

なんで書かなくなったんだろうなーと思い返すと

  • 面白いようなことがあってもまとめる時間が作れなかったり
  • そのうち忘れたり
  • やってることが公開できそうにないことだったり
  • どーでもいいことに悩んだり

とかとか、他人に有益にならんことばっかで愚痴っぽくなりそうだったんですよ!

と、言い訳しておく!結局はめんどくさいに勝てなかっただけ

これからは、どんどんグダグダいいながらコード書くよ!

前向きにね!