Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
2018-11-10 Akka-HTTPで作るAPIサーバ
Search
kamijin_fanta
November 10, 2018
Technology
4.2k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
2018-11-10 Akka-HTTPで作るAPIサーバ
https://github.com/kamijin-fanta/slide/tree/master/2018-11-10%20ScalaKansai-AkkaHTTP
kamijin_fanta
November 10, 2018
More Decks by kamijin_fanta
See All by kamijin_fanta
2026-06-11 Iceberg Trinoログ基盤の 設計ポイント - Design Points for an Iceberg + Trino Log Platform
kamijin_fanta
0
14
2025-09-22 Iceberg, Trinoでのログ基盤構築と パフォーマンス最適化
kamijin_fanta
1
870
2025-04-14 Data & Analytics 井戸端会議 Multi tenant log platform with Iceberg
kamijin_fanta
1
780
IoT向けストレージにTiKVを採用したときの話 / 2024-10-25 TiUG Meetup 3 Using TiKV as IoT storage
kamijin_fanta
0
190
TrinoとIcebergで ログ基盤の構築 / 2023-10-05 Trino Presto Meetup
kamijin_fanta
1
2.7k
Unicodeと符号化形式
kamijin_fanta
0
1.2k
Reactとフォームとスキーマバリデーション / React forms with Schema Validation
kamijin_fanta
0
2.7k
2020/05/25 さくらのクラウド向けツールを使いこなす
kamijin_fanta
3
380
2019-01-24 業務でのOSSとの関わり方
kamijin_fanta
7
5k
Other Decks in Technology
See All in Technology
Kiro WebとCloud Sessions
nagisa53
2
200
アクセスキーが漏れた日にやるべきこと- 無効化の先にある本当の対応
kazzpapa3
0
320
あなたの知らないバージョン命名規則
sat
PRO
2
740
わたしが知り合いゼロの勉強会に 行けるようになるまで
r5ni4
2
790
Sansan Engineering Unit 紹介資料
sansan33
PRO
1
5k
markdown-poster Introduction
kazamori
0
130
AI開発に用いられるHPC技術について
gpuunite_official
0
280
AIエージェントのためのデータ設計
daiz21
0
220
ハッカソンで入賞した話 @ Findy LT
asari194617
0
1.5k
Rust×eBPFでEDRっぽいものをつくる
sunlife3
2
970
AWSとGitHub Actionsの責任境界と 組織で安全に使用する取り組み
nealle
0
150
電話に出る Python のログの話
shinnosuke_kishida
0
220
Featured
See All Featured
Design in an AI World
tapps
1
290
A Soul's Torment
seathinner
6
3.5k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
The Curse of the Amulet
leimatthew05
2
14k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
870
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
550
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
Making Projects Easy
brettharned
120
6.7k
Fireside Chat
paigeccino
42
4k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
1
380
The Invisible Side of Design
smashingmag
301
52k
Transcript
Akka‑HTTPで作るAPIサーバ 2018‑11‑10 Scala Kansai Summit ‑ kamijin‑fanta
諸注意 スライド公開します 撮影NG (イベントのレギュレーション) Twitter: #scala̲ks #s1 2
自己紹介 Scala関西Summit運営 Github: kamijin‑fanta Twitter: kamijin̲fanta 3
Akka HTTP HTTP Server HTMLテンプレート・フォーム等を使用したWebサイト クライアント・バックエンドが分離したAPIサーバ HTTP Client スクレイピング マイクロサービスの他のエンドポイントの呼び出し
HTTP Serverについて主に紹介 4
Akka HTTP is... Webフレームワーク 例 Play, Rails, Django, Laravel 機能
ルーティング・テンプレート・データベース・セッション・認証・テスト 5
Akka HTTP is HTTP Library Akka HTTPはWebフレームワークではない 有: ルーティング・テスト 無:
テンプレート・データベース・セッション・認証 6
Why Akka HTTP 何故機能の少ないAkka HTTPを選択するのか Webフレームワークが適切でないユースケースが存在 大きなビジネスロジックが存在し、APIが付加的なものである RDB(MySQL等)ではなくKVS(Dynamo等)を使用する 認証に独自のSSOを使用する 気に入ったライブラリ(twirl,
circe等)を使用したい フレームワークのアップグレードコストが無視できない プログラミングの考え方 他のWebフレームワーク: HTTPのサービスを記述する Akka HTTP: アプリケーションを記述し、HTTPと統合する 7
Beginner friendly Akka HTTPは2つの側面が有る 大規模サービス構築に向く DB・セッション管理なども1から作れる クリーンアーキテクチャ等の設計手法と親和性が高い 単機能なので破壊的変更が少なく、アップグレードコストが低い 単機能で覚える機能が少ない Scalaの習得後のステップで扱うのに適している
8
Recommended for... こんな人におすすめ フレームワークが負担になってきた アップグレード・独自の認証・スケーリング シンプルな機能の組み合わせでHTTPサーバを作りたい 単機能なディレクティブを組み合わせる Scalaの言語をある程度覚え、何を作ろうか迷っている ライブラリ特有の機能等の覚えるべきことが少ない 9
Understand code 10
Install Requirement sbt JDK // build.sbt libraryDependencies ++= Seq( "com.typesafe.akka"
%% "akka‐http" % "10.1.5", "com.typesafe.akka" %% "akka‐stream" % "2.5.12", ) 11
Basic Server object HttpServerUseHttpApp { object WebServer extends HttpApp {
override def routes: Route = path("hello") { get { val entity = HttpEntity( ContentTypes.`text/html(UTF‐8)`, "<h1>hello akka‐http</h1>" ) complete(entity) } } } def main(args: Array[String]): Unit = WebServer.startServer("localhost", 8080) } 12
$ sbt > run $ curl localhost:8080/hello <h1>hello akka‐http</h1> 13
Model, Types リクエスト・レスポンスに対応する型が定義されている Request, Response, Uri, Header(Accept, Cookie) etc... Scalaのパターンマッチングを行うことができる
例:Reqに含まれるAcceptヘッダからUEで許可されているMIMEタイプを抽出 多数の型が定義されているので、一部だけ紹介 14
Methods HttpMethods.CONNECT HttpMethods.DELETE HttpMethods.GET HttpMethods.HEAD HttpMethods.OPTIONS HttpMethods.PATCH HttpMethods.POST HttpMethods.PUT HttpMethods.TRACE
15
Uri / Query assert( Uri("https://akka.io:443/try‐akka/") === Uri.from(scheme = "https", host
= "akka.io", port = 443, path = "/try‐akka/") ) assert(Query("key1=value1&key2=Foo&key2=Bar").get("key1") === Some("value1")) assert(Query("key=Foo&key=Bar").getAll("key") === List("Bar", "Foo")) 16
Headers RawHeader("x‐custom‐header", "value") headers.Host("example.com") headers.`Access‐Control‐Allow‐Origin`(HttpOrigin("https://example.com")) 17
Routing DSL object RoutingBasic extends HttpApp { def main(args: Array[String]):
Unit = startServer("localhost", 8080) override def routes: Route = pathPrefix("hello") { get { complete("get hello") } ~ post { complete("post hello") } } ~ ((get | post) & path("user" / Segment)) { userName => complete(s"UserName: $userName") } } Directive(pathPrefix, get, post, complete, path)を組み合わせてRouteを作る 入れ子にすることや、 ~ & | で連結することが出来る 18
What is Directive? // 1. 内部のルートに委譲または、拒否しフィルタリングを行う filter(args...) { ??? }
// 2. 値を抽出し、内部のルートに渡す extract(args...) { variable => ??? } // 3. コンテンツを返す complate(???) 2つ以上の性質を組み合わせたディレクティブも存在 19
Composing Route override def routes: Route = pathPrefix("hello") { get
{ complete("get hello") } ~ post { complete("post hello") } } ~ ((get | post) & path("user" / Segment)) { userName => complete(s"UserName: $userName") } ~ : ルートで拒否されたときに、次のRouteへ処理を移す & : 両方のディレクティブの条件を満たす必要がある | : どちらかのディレクティブの条件を満たす必要がある 20
val getOrPostUser = (get | post) & path("user" / Segment)
override def routes: Route = pathPrefix("hello") { get { complete("get hello") } ~ post { complete("post hello") } } ~ getOrPostUser { userName => complete(s"UserName: $userName") } 21
Test Kit libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka‐http‐testkit" % "10.1.3"
% Test, "org.scalatest" %% "scalatest" % "3.0.5" % Test ) class SimpleHttpServerSpec extends FunSpec with ScalatestRouteTest { it("basic test") { val route = path("hello") & get { complete(HttpEntity(ContentTypes.`text/html(UTF‐8)`, "<h1>Say hello to akka‐http</h1>")) } Get("/hello") ~> route ~> check { assert(status === StatusCodes.OK) assert(responseAs[String] === "<h1>Say hello to akka‐http</h1>") assert(contentType === ContentTypes.`text/html(UTF‐8)`) } } } 22
Get("/hello") はHttpRequestを作るためのショートハンド Get Post Put Patch Delete Options Head もある
object HttpRequest { def apply( method: HttpMethod = HttpMethods.GET, uri: Uri = Uri./, headers: immutable.Seq[HttpHeader] = Nil, entity: RequestEntity = HttpEntity.Empty, protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) = new HttpRequest(method, } 23
Easy to test DirectiveはRequestから値の抽出・フィルタリングを行う純粋関数→テストしやすい 任意のHttpRequestをRouteに投げた結果を検証する仕組みがTestKitで用意されている 簡易なテストを楽に書くためのショートハンドも存在 24
Rejection override def routes: Route = path("hello") { get {
complete("get hello") } ~ post { complete("post hello") } } $ curl ‐X DELETE localhost:8080/hello HTTP method not allowed, supported methods: GET, POST 適切なエラーをクライアントに返すための仕組み 25
Provide Rejections override def routes: Route = path("foo") { reject
} ~ path("bar") { reject(MissingQueryParamRejection("rejection reason")) } final case class MissingQueryParamRejection(parameterName: String) extends jserver.MissingQueryParamRejection with Rejection trait Rejection $ curl localhost:8080/bar Request is missing required query parameter 'rejection reason' 26
Handle Rejection 標準ではデフォルトのRejectionHandlerが使用されている 上書きor拡張することで、カスタムのエラーメッセージを返すことが可能 Route.seal でラップし、implicit valを注入する 27
implicit def myRejectionHandler: RejectionHandler = // point 1 RejectionHandler.newBuilder() .handle
{ case reject: MissingQueryParamRejection => complete( StatusCodes.BadRequest, s"required query parameter [${reject.parameterName}]") }.result() override def routes: Route = Route.seal(internalRoutes) // point 2 def internalRoutes: Route = path("bar") { reject(MissingQueryParamRejection("rejection reason")) } final case class MissingQueryParamRejection(parameterName: String) extends jserver.MissingQueryParamRejection with Rejection 28
エラーを型を使って表現することが出来る RejectionHandlerで、エラーメッセージを容易にカスタマイズ可能 JSON/HTML/XMLでのエラー対応等 29
Marshall Marshal オブジェクトをシリアライズする仕組み 文字列・バイト列などに変換するMarshallerを定義する 30
import io.circe.generic.auto._ import io.circe.syntax._ case class User(name: String, age: Int)
implicit val userMarshaller: ToEntityMarshaller[User] = Marshaller.opaque { user => HttpEntity(ContentTypes.`application/json`, user.asJson.noSpaces) } val route: Route = get { complete(User("mika", 20)) } Get("/") ~> route ~> check { assert(contentType === ContentTypes.`application/json`) assert(responseAs[String] === """{"name":"mika","age":20}""") } 31
Examples https://github.com/kamijin‑fanta/akka‑http‑ 2018/tree/master/src/main/scala/examples https://github.com/kamijin‑fanta/akka‑http‑ 2018/tree/master/src/test/scala/examples 32
Summary Route DSL Model Directive Rejection Marshall Test Kit Akka
HTTPはこれらのパーツを組み合わせ、 シンプルなHTTPサーバ構築をサポートするライブラリ 33
https://kinyoubenkyokai.github.io/book/techbook05/ 34
ありがとうございました Akka-HTTP で作るAPI サーバ 2018‑11‑10 Scala Kansai Summit ‑ kamijin‑fanta
35