0Pricing
Scala for Backend Engineering & Functional Programming · レッスン

JSONエンドポイント

http4sとCirceを組み合わせます。

「JSONエンドポイント」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

circeによるJSON

http4sはhttp4s-circeモジュールを通じてJSONと統合されており、circeのコーデックとEntityDecoderおよびEntityEncoderを橋渡しします。circeは、Scalaで事実上の標準となっている関数型JSONライブラリです。

自分の型にEncoderとDecoderのインスタンスを定義すれば、http4sがワイヤーフォーマットを処理します。

// build.sbt
// "org.http4s" %% "http4s-circe" % http4sV
// "io.circe"   %% "circe-generic" % circeV

コーデックの自動導出

circe-genericを使うと、deriveEncoder/deriveDecoderや@JsonCodecアノテーションによってコーデックを自動的に導出できます。これらはケースクラスのフィールドを、名前に基づいてJSONキーに対応付けます。

導出するには、すべてのフィールド型についてコーデックが再帰的に必要です。

import io.circe.generic.semiauto._

case class User(id: Int, name: String)
implicit val enc = deriveEncoder[User]
implicit val dec = deriveDecoder[User]

circeからのEntityEncoder

org.http4s.circe.CirceEntityEncoder._をインポートすると、circeのEncoderを持つ任意のAに対して暗黙のEntityEncoder[F, A]が提供されます。これにより、Ok(user)はapplication/jsonとしてJSONにシリアライズされます。

ハンドラーでJsonへ手動変換する必要はありません。

import org.http4s.circe.CirceEntityEncoder._

HttpRoutes.of[IO] {
  case GET -> Root / "me" => Ok(User(1, "Ada"))
}

circeからのEntityDecoder

逆方向も同様です。org.http4s.circe.CirceEntityDecoder._をインポートすると、circeのDecoderを持つ任意のAに対してEntityDecoder[F, A]が提供されます。これにより、req.as[User]でJSONボディをパースできます。

両方のインポートはCirceEntityCodec._にまとめられています。

import org.http4s.circe.CirceEntityDecoder._

case req @ POST -> Root / "users" =>
  req.as[User].flatMap(u => Created(u))

完全なJSON POST

デコードとエンコードを組み合わせると、すっきりした作成エンドポイントになります。受信したJSONをドメイン型にパースし、ビジネスロジックを実行してから、結果をJSONとして再びエンコードします。

デコードの失敗は、circeのデコーダーによって422または400として返されます。

import org.http4s.circe.CirceEntityCodec._

case req @ POST -> Root / "users" =>
  for {
    in  <- req.as[User]
    out <- store.create(in)
    res <- Created(out)
  } yield res

jsonインターポレーター

その場でJSONを作成する場合は、io.circe.literalにあるcirceのjson文字列インターポレーターを使うと、Scalaの値を埋め込みながらJson値を直接構築できます。

ケースクラスを用意せずに小さなレスポンスやテスト用フィクスチャを作成する場合に便利です。

import io.circe.literal._

val body = json"""{ "status": "ok", "count": 3 }"""
Ok(body)

カスタムフィールド名

JSONキーがScalaのフィールド名と異なる場合は、circe-generic-extrasのConfiguration(例:snake_case)を使って導出するか、forProduct2でコーデックを手動で記述します。

これにより、APIの契約を内部の命名規則から切り離せます。

import io.circe.Encoder

implicit val e: Encoder[User] =
  Encoder.forProduct2("user_id", "full_name")(u => (u.id, u.name))

リストのエンコード

circeは、要素のコーデックが存在すれば、List、Vector、Option、Mapのコーデックを自動的に提供します。そのため、コレクションを返すだけで機能します。

空のリストは[]にエンコードされ、Noneはキーなし、またはnullにエンコードされます。

import org.http4s.circe.CirceEntityEncoder._

case GET -> Root / "users" =>
  store.all.flatMap(us => Ok(us)) // List[User] -> JSON array

デコードしたJSONの検証

デコードによって得られるのは構文的に正しい値です。意味的な検証は自分で行う必要があります。asの後に検査を実行し、無効な場合はBadRequestで処理を短絡させます。

ドメイン不変条件は、明確さを保つためにEitherを返すスマートコンストラクターにまとめてください。

req.as[User].flatMap { u =>
  if (u.name.nonEmpty) Created(u)
  else BadRequest("name required")
}

JSONとしてのエラーレスポンス

クライアントがパースできるように、構造化されたエラーをJSONで返します。エラー用のケースクラスにcirceのエンコーダーを定義し、該当するステータスビルダーに渡します。

エラーの形式を統一すると、APIははるかに利用しやすくなります。

case class ApiError(code: String, message: String)
implicit val e = deriveEncoder[ApiError]

BadRequest(ApiError("E_NAME", "name required"))

ストリーミングJSON

大規模なコレクションでは、バッファーに蓄積せずJSONをストリーミングできます。jsonEncoderOfとfs2のStream[F, A]を組み合わせると、JSON配列を段階的に出力できます。

これにより、大量の結果セットでもメモリ使用量を一定に保ち、fs2のバックプレッシャーを活用できます。

import org.http4s.circe.streamJsonArrayEncoder

case GET -> Root / "feed" =>
  Ok(store.streamAll) // fs2.Stream[IO, Event]

クイックチェック

JSONボディをケースクラスにデコードできるようにするインポートを思い出してください。

まとめ

http4s-circeでJSONを接続し、コーデックを導出してCirceEntityCodecをインポートし、Ok(value)とreq.as[A]でJSONの入出力全体を処理しました。

リスト、カスタムフィールド名、構造化されたJSONエラーを扱い、大きなペイロード向けの配列のストリーミングも実装しました。

よくある質問

「JSONエンドポイント」レッスンは無料ですか?

はい。「JSONエンドポイント」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

「JSONエンドポイント」で何を学びますか?

http4sとCirceを組み合わせます。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「JSONエンドポイント」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. RoutesとHttpRoutes
  2. リクエストとレスポンス
  3. JSONエンドポイント
  4. アプリケーションを提供する
← Scala for Backend Engineering & Functional Programmingに戻る