0
# Response Generation
1
2
Complete HTTP status code constants and response builders for creating well-formed HTTP responses. The DSL provides fluent APIs for all standard HTTP status codes with appropriate response body and header handling.
3
4
## Capabilities
5
6
### Response Generator Traits
7
8
Base traits for building HTTP responses with different characteristics.
9
10
```scala { .api }
11
/**
12
* Base trait for all response generators
13
*/
14
trait ResponseGenerator extends Any {
15
def status: Status
16
}
17
18
/**
19
* Generator for responses without body content
20
*/
21
trait EmptyResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
22
def apply()(implicit F: Applicative[F]): F[Response[G]]
23
def headers(header: Header.ToRaw, _headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
24
}
25
26
/**
27
* Generator for responses with optional body content
28
*/
29
trait EntityResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
30
def liftG: FunctionK[G, F]
31
32
def apply()(implicit F: Applicative[F]): F[Response[G]]
33
def apply[A](body: G[A])(implicit F: Monad[F], w: EntityEncoder[G, A]): F[Response[G]]
34
def apply[A](body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
35
def headers(header: Header.ToRaw, _headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
36
}
37
38
/**
39
* Generator for redirect responses requiring Location header
40
*/
41
trait LocationResponseGenerator[F[_], G[_]] extends Any with EntityResponseGenerator[F, G] {
42
def apply(location: Location)(implicit F: Applicative[F]): F[Response[G]]
43
def apply[A](location: Location, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
44
}
45
46
/**
47
* Generator for 401 Unauthorized responses requiring WWW-Authenticate header
48
*/
49
trait WwwAuthenticateResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
50
def apply(authenticate: `WWW-Authenticate`, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
51
def apply[A](authenticate: `WWW-Authenticate`, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
52
}
53
54
/**
55
* Generator for 405 Method Not Allowed responses requiring Allow header
56
*/
57
trait AllowResponseGenerator[F[_], G[_]] extends Any with ResponseGenerator {
58
def apply(allow: Allow, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
59
def apply[A](allow: Allow, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
60
}
61
```
62
63
### Success Responses (2xx)
64
65
Status codes indicating successful request processing.
66
67
```scala { .api }
68
// 2xx Success Status Codes
69
val Ok: Status.Ok.type // 200
70
val Created: Status.Created.type // 201
71
val Accepted: Status.Accepted.type // 202
72
val NonAuthoritativeInformation: Status.NonAuthoritativeInformation.type // 203
73
val NoContent: Status.NoContent.type // 204
74
val ResetContent: Status.ResetContent.type // 205
75
val PartialContent: Status.PartialContent.type // 206
76
val MultiStatus: Status.MultiStatus.type // 207
77
val AlreadyReported: Status.AlreadyReported.type // 208
78
val IMUsed: Status.IMUsed.type // 226
79
```
80
81
**Usage Examples:**
82
83
```scala
84
import org.http4s.dsl.io._
85
import cats.effect.IO
86
87
val routes = HttpRoutes.of[IO] {
88
// Simple OK response
89
case GET -> Root / "hello" =>
90
Ok("Hello, World!")
91
92
// Created with response body
93
case POST -> Root / "users" =>
94
Created("User created successfully")
95
96
// No content response
97
case DELETE -> Root / "users" / IntVar(id) =>
98
NoContent()
99
100
// Response with custom headers
101
case GET -> Root / "data" =>
102
Ok("Response data", Header("X-Custom", "value"))
103
104
// JSON response (with appropriate EntityEncoder)
105
case GET -> Root / "api" / "user" / IntVar(id) =>
106
val user = User(id, "John Doe")
107
Ok(user.asJson)
108
}
109
```
110
111
### Redirection Responses (3xx)
112
113
Status codes for URL redirection with automatic Location header handling.
114
115
```scala { .api }
116
// 3xx Redirection Status Codes
117
val MultipleChoices: Status.MultipleChoices.type // 300
118
val MovedPermanently: Status.MovedPermanently.type // 301
119
val Found: Status.Found.type // 302
120
val SeeOther: Status.SeeOther.type // 303
121
val NotModified: Status.NotModified.type // 304
122
val TemporaryRedirect: Status.TemporaryRedirect.type // 307
123
val PermanentRedirect: Status.PermanentRedirect.type // 308
124
```
125
126
**Usage Examples:**
127
128
```scala
129
import org.http4s.dsl.io._
130
import org.http4s.headers.Location
131
import org.http4s.Uri
132
133
val routes = HttpRoutes.of[IO] {
134
// Permanent redirect
135
case GET -> Root / "old-path" =>
136
MovedPermanently(Location(Uri.unsafeFromString("/new-path")))
137
138
// Temporary redirect with body
139
case GET -> Root / "temp" =>
140
Found(Location(Uri.unsafeFromString("/temporary-location")), "Redirecting...")
141
142
// Post-redirect-get pattern
143
case POST -> Root / "submit" =>
144
SeeOther(Location(Uri.unsafeFromString("/success")))
145
146
// Not modified (for caching)
147
case GET -> Root / "resource" if notModified =>
148
NotModified()
149
}
150
```
151
152
### Client Error Responses (4xx)
153
154
Status codes indicating client-side errors with specialized response generators.
155
156
```scala { .api }
157
// 4xx Client Error Status Codes
158
val BadRequest: Status.BadRequest.type // 400
159
val Unauthorized: Status.Unauthorized.type // 401
160
val PaymentRequired: Status.PaymentRequired.type // 402
161
val Forbidden: Status.Forbidden.type // 403
162
val NotFound: Status.NotFound.type // 404
163
val MethodNotAllowed: Status.MethodNotAllowed.type // 405
164
val NotAcceptable: Status.NotAcceptable.type // 406
165
val ProxyAuthenticationRequired: Status.ProxyAuthenticationRequired.type // 407
166
val RequestTimeout: Status.RequestTimeout.type // 408
167
val Conflict: Status.Conflict.type // 409
168
val Gone: Status.Gone.type // 410
169
val LengthRequired: Status.LengthRequired.type // 411
170
val PreconditionFailed: Status.PreconditionFailed.type // 412
171
val PayloadTooLarge: Status.PayloadTooLarge.type // 413
172
val UriTooLong: Status.UriTooLong.type // 414
173
val UnsupportedMediaType: Status.UnsupportedMediaType.type // 415
174
val RangeNotSatisfiable: Status.RangeNotSatisfiable.type // 416
175
val ExpectationFailed: Status.ExpectationFailed.type // 417
176
val MisdirectedRequest: Status.MisdirectedRequest.type // 421
177
val UnprocessableEntity: Status.UnprocessableEntity.type // 422
178
val Locked: Status.Locked.type // 423
179
val FailedDependency: Status.FailedDependency.type // 424
180
val TooEarly: Status.TooEarly.type // 425
181
val UpgradeRequired: Status.UpgradeRequired.type // 426
182
val PreconditionRequired: Status.PreconditionRequired.type // 428
183
val TooManyRequests: Status.TooManyRequests.type // 429
184
val RequestHeaderFieldsTooLarge: Status.RequestHeaderFieldsTooLarge.type // 431
185
val UnavailableForLegalReasons: Status.UnavailableForLegalReasons.type // 451
186
```
187
188
**Usage Examples:**
189
190
```scala
191
import org.http4s.dsl.io._
192
import org.http4s.headers.{`WWW-Authenticate`, Allow}
193
194
val routes = HttpRoutes.of[IO] {
195
// Bad request with validation errors
196
case POST -> Root / "users" =>
197
// Validation logic
198
if (invalidInput) {
199
BadRequest("Invalid user data provided")
200
} else {
201
Created("User created")
202
}
203
204
// Unauthorized with WWW-Authenticate header
205
case GET -> Root / "protected" =>
206
Unauthorized(`WWW-Authenticate`(Challenge("Bearer", "api")))
207
208
// Not found with helpful message
209
case GET -> Root / "users" / IntVar(id) =>
210
// Lookup user
211
userService.findById(id).flatMap {
212
case Some(user) => Ok(user.asJson)
213
case None => NotFound(s"User with ID $id not found")
214
}
215
216
// Method not allowed with Allow header
217
case req @ POST -> Root / "readonly" =>
218
MethodNotAllowed(Allow(Set(GET, HEAD)))
219
220
// Conflict for duplicate resources
221
case POST -> Root / "users" =>
222
// Check for existing user
223
if (userExists) {
224
Conflict("User already exists")
225
} else {
226
Created("User created")
227
}
228
}
229
```
230
231
### Server Error Responses (5xx)
232
233
Status codes indicating server-side errors and service unavailability.
234
235
```scala { .api }
236
// 5xx Server Error Status Codes
237
val InternalServerError: Status.InternalServerError.type // 500
238
val NotImplemented: Status.NotImplemented.type // 501
239
val BadGateway: Status.BadGateway.type // 502
240
val ServiceUnavailable: Status.ServiceUnavailable.type // 503
241
val GatewayTimeout: Status.GatewayTimeout.type // 504
242
val HttpVersionNotSupported: Status.HttpVersionNotSupported.type // 505
243
val VariantAlsoNegotiates: Status.VariantAlsoNegotiates.type // 506
244
val InsufficientStorage: Status.InsufficientStorage.type // 507
245
val LoopDetected: Status.LoopDetected.type // 508
246
val NotExtended: Status.NotExtended.type // 510
247
val NetworkAuthenticationRequired: Status.NetworkAuthenticationRequired.type // 511
248
```
249
250
**Usage Examples:**
251
252
```scala
253
import org.http4s.dsl.io._
254
255
val routes = HttpRoutes.of[IO] {
256
// Handle application errors
257
case GET -> Root / "data" =>
258
dataService.fetchData().attempt.flatMap {
259
case Right(data) => Ok(data.asJson)
260
case Left(error) =>
261
logger.error("Data fetch failed", error)
262
InternalServerError("Unable to fetch data")
263
}
264
265
// Service temporarily unavailable
266
case GET -> Root / "maintenance" =>
267
ServiceUnavailable("Service under maintenance")
268
269
// Not implemented features
270
case PUT -> Root / "beta-feature" =>
271
NotImplemented("Feature not yet implemented")
272
}
273
```
274
275
### Informational Responses (1xx)
276
277
Status codes for informational responses (less commonly used).
278
279
```scala { .api }
280
// 1xx Informational Status Codes
281
val Continue: Status.Continue.type // 100
282
val SwitchingProtocols: Status.SwitchingProtocols.type // 101
283
val Processing: Status.Processing.type // 102
284
val EarlyHints: Status.EarlyHints.type // 103
285
```
286
287
288
### Content Type and Encoding
289
290
Response generators automatically handle content types and encoding based on the `EntityEncoder` instances.
291
292
```scala
293
import org.http4s.dsl.io._
294
import org.http4s.circe._
295
import io.circe.generic.auto._
296
297
val routes = HttpRoutes.of[IO] {
298
// JSON response (with circe EntityEncoder)
299
case GET -> Root / "api" / "users" =>
300
val users = List(User("Alice", 25), User("Bob", 30))
301
Ok(users.asJson) // Content-Type: application/json
302
303
// Plain text response
304
case GET -> Root / "health" =>
305
Ok("OK") // Content-Type: text/plain
306
307
// HTML response
308
case GET -> Root =>
309
Ok("<html><body><h1>Welcome</h1></body></html>")
310
.map(_.withContentType(`Content-Type`(MediaType.text.html)))
311
}
312
```
313
314
## Error Handling Best Practices
315
316
The DSL enables consistent error handling patterns:
317
318
```scala
319
import org.http4s.dsl.io._
320
321
val routes = HttpRoutes.of[IO] {
322
case GET -> Root / "users" / IntVar(id) =>
323
userService.findById(id).flatMap {
324
case Some(user) => Ok(user.asJson)
325
case None => NotFound(s"User $id not found")
326
}.handleErrorWith { error =>
327
logger.error(s"Error fetching user $id", error)
328
InternalServerError("Internal server error")
329
}
330
}
331
```
332
333
## Type Safety
334
335
- All status constants are singleton types for compile-time safety
336
- Response generators preserve effect type `F[_]` and entity type information
337
- EntityEncoder instances ensure type-safe response body serialization
338
- Headers are type-safe through the http4s header system