3. What is spray?
Suite of libraries for building and consuming
RESTful web services on top of Akka
4. What is spray?
Suite of libraries for building and consuming
RESTful web services on top of Akka
• First released about 1 year ago
5. What is spray?
Suite of libraries for building and consuming
RESTful web services on top of Akka
• First released about 1 year ago
• Principles: lightweight, async, non-blocking,
actor-based, modular, few deps, testable
6. What is spray?
Suite of libraries for building and consuming
RESTful web services on top of Akka
• First released about 1 year ago
• Principles: lightweight, async, non-blocking,
actor-based, modular, few deps, testable
• Philosophy: library, not framework
10. Components
• Rich immutable HTTP model
• spray-server:
DSL for server-side API construction
• spray-client: complementary HTTP client
11. Components
• Rich immutable HTTP model
• spray-server:
DSL for server-side API construction
• spray-client: complementary HTTP client
• spray-can: low-level HTTP server and client
12. Components
• Rich immutable HTTP model
• spray-server:
DSL for server-side API construction
• spray-client: complementary HTTP client
• spray-can: low-level HTTP server and client
• spray-json: straight JSON in scala (no Akka)
15. spray-server
• Runs on servlet containers or spray-can
• Tool for building a “self-contained” API layer
16. spray-server
• Runs on servlet containers or spray-can
• Tool for building a “self-contained” API layer
• Central element:
Routing DSL for defining web API behavior
17. spray-server
• Runs on servlet containers or spray-can
• Tool for building a “self-contained” API layer
• Central element:
Routing DSL for defining web API behavior
• Focus: RESTful web API, not web GUI
28. API Layer Responsibilities
• Request routing based on method, path,
query parameters, entity
• (Un)marshalling to / from domain objects
29. API Layer Responsibilities
• Request routing based on method, path,
query parameters, entity
• (Un)marshalling to / from domain objects
• Encoding / decoding
30. API Layer Responsibilities
• Request routing based on method, path,
query parameters, entity
• (Un)marshalling to / from domain objects
• Encoding / decoding
• Authentication / authorization
31. API Layer Responsibilities
• Request routing based on method, path,
query parameters, entity
• (Un)marshalling to / from domain objects
• Encoding / decoding
• Authentication / authorization
• Caching and serving static content
32. API Layer Responsibilities
• Request routing based on method, path,
query parameters, entity
• (Un)marshalling to / from domain objects
• Encoding / decoding
• Authentication / authorization
• Caching and serving static content
• RESTful error handling
33. Route Example
A simple spray route:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}
}~
put {
completeWith {
"Received PUT request for order " + id
}
}
}
38. Routing Basics
The simplest route:
ctx => ctx.complete("Say hello to spray")
or:
_.complete("Say hello to spray")
39. Routing Basics
The simplest route:
ctx => ctx.complete("Say hello to spray")
or:
_.complete("Say hello to spray")
or using a “directive”:
completeWith("Say hello to spray")
40. Routing Basics
The simplest route:
ctx => ctx.complete("Say hello to spray")
or:
_.complete("Say hello to spray")
or using a “directive”:
completeWith("Say hello to spray")
def completeWith[T :Marshaller](value: => T): Route =
_.complete(value)
41. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}
}~
put {
completeWith {
"Received PUT request for order " + id
}
}
}
42. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}
directive
}~
name put {
completeWith {
"Received PUT request for order " + id
}
}
}
43. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}
}~
args
put {
completeWith {
"Received PUT request for order " + id
}
}
}
44. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get { extractions
completeWith {
"Received GET request for order " + id
}
}~
put {
completeWith {
"Received PUT request for order " + id
}
}
}
45. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}
}~
put {
completeWith {
"Received PUT request for order " + id
}
} inner route
}
46. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}~
} route concatenation:
put { recover from rejections
completeWith {
"Received PUT request for order " + id
}
}
}
47. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}
}~
put {
completeWith {
"Received PUT request for order " + id
}
}
}
48. Directives
Route structure built with directives:
val route: Route =
path("order" / HexIntNumber) { id =>
get {
completeWith {
"Received GET request for order " + id
}
}~
put {
completeWith {
"Received PUT request for order " + id
}
} Route structure
}
forms a tree!
49. Directives
DRYing up with the `|` operator:
val route =
path("order" / HexIntNumber) { id =>
(get | put) { ctx =>
ctx.complete("Received " + ctx.request.method +
" request for order " + id)
}
}
50. Directives
Pulling out a custom directive:
val getOrPut = get | put
val route =
path("order" / HexIntNumber) { id =>
getOrPut { ctx =>
ctx.complete("Received " + ctx.request.method +
" request for order " + id)
}
}
51. Directives
The `&` operator as alternative to nesting:
val getOrPut = get | put
val route =
(path("order" / HexIntNumber) & getOrPut) { id =>
ctx =>
ctx.complete("Received " + ctx.request.method +
" request for order " + id)
}
52. Directives
Pulling out once more:
val orderGetOrPut =
path("order" / HexIntNumber) & (get | put)
val route =
orderGetOrPut { id => ctx =>
ctx.complete("Received " + ctx.request.method +
" request for order " + id)
}
57. Directives
Compiles?
Operators are type-safe:
val orderPath = path("order" / IntNumber)
val dir = orderPath | get
val dir = orderPath | path("[^/]+".r / DoubleNumber)
58. Directives
Compiles?
Operators are type-safe:
val orderPath = path("order" / IntNumber)
val dir = orderPath | get
val dir = orderPath | path("[^/]+".r / DoubleNumber)
59. Directives
Compiles?
Operators are type-safe:
val orderPath = path("order" / IntNumber)
val dir = orderPath | get
val dir = orderPath | path("[^/]+".r / DoubleNumber)
val dir = orderPath | parameter('order.as[Int])
60. Directives
Compiles?
Operators are type-safe:
val orderPath = path("order" / IntNumber)
val dir = orderPath | get
val dir = orderPath | path("[^/]+".r / DoubleNumber)
val dir = orderPath | parameter('order.as[Int])
61. Directives
Compiles?
Operators are type-safe:
val orderPath = path("order" / IntNumber)
val dir = orderPath | get
val dir = orderPath | path("[^/]+".r / DoubleNumber)
val dir = orderPath | parameter('order.as[Int])
val order = orderPath & parameters('oem, 'expired ?)
62. Directives
Compiles?
Operators are type-safe:
val orderPath = path("order" / IntNumber)
val dir = orderPath | get
val dir = orderPath | path("[^/]+".r / DoubleNumber)
val dir = orderPath | parameter('order.as[Int])
val order = orderPath & parameters('oem, 'expired ?)
63. Directives
Compiles?
Operators are type-safe:
val orderPath = path("order" / IntNumber)
val dir = orderPath | get
val dir = orderPath | path("[^/]+".r / DoubleNumber)
val dir = orderPath | parameter('order.as[Int])
val order = orderPath & parameters('oem, 'expired ?)
val route = order { (orderId, oem, expired) =>
... // inner route
}
69. Best Practices
• Keep route structure clean and readable,
pull out all logic into custom directives
70. Best Practices
• Keep route structure clean and readable,
pull out all logic into custom directives
• Don’t let API layer leak into application
71. Best Practices
• Keep route structure clean and readable,
pull out all logic into custom directives
• Don’t let API layer leak into application
• Use (Un)marshalling infrastructure
72. Best Practices
• Keep route structure clean and readable,
pull out all logic into custom directives
• Don’t let API layer leak into application
• Use (Un)marshalling infrastructure
• Wrap blocking code with `detach`
73. Best Practices
• Keep route structure clean and readable,
pull out all logic into custom directives
• Don’t let API layer leak into application
• Use (Un)marshalling infrastructure
• Wrap blocking code with `detach`
• Use sbt-revolver + JRebel for fast dev turn-
around
82. Current State
• spray 0.9.0 just released
(last release for Akka 1.x)
• Current focus: Release first Akka 2.0
compatible milestone of spray 1.0
83. Current State
• spray 0.9.0 just released
(last release for Akka 1.x)
• Current focus: Release first Akka 2.0
compatible milestone of spray 1.0
• Coming features: new documentation site,
deeper REST support, monitoring, request
throttling, and more ...