0
# Status Codes and Response Generation
1
2
Complete set of HTTP status codes with fluent response generation API. Each status code provides type-safe methods for creating responses with or without bodies, headers, and proper content negotiation.
3
4
## Capabilities
5
6
### Response Generation Pattern
7
8
All HTTP status codes follow a consistent pattern for response generation:
9
10
```scala { .api }
11
/**
12
* Base response generator trait
13
*/
14
trait ResponseGenerator {
15
def status: Status
16
}
17
18
/**
19
* Response generator for responses without body content
20
*/
21
trait EmptyResponseGenerator[F[_], G[_]] extends 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
* Response generator for responses with optional body content
28
*/
29
trait EntityResponseGenerator[F[_], G[_]] extends ResponseGenerator {
30
def liftG: G ~> F
31
def apply()(implicit F: Applicative[F]): F[Response[G]]
32
def apply[A](body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
33
def headers(header: Header.ToRaw, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
34
}
35
```
36
37
### 1xx Informational Responses
38
39
Informational status codes for ongoing request processing.
40
41
```scala { .api }
42
/**
43
* 100 Continue - Request headers received, client should send body
44
*/
45
val Continue: Status.Continue.type
46
47
/**
48
* 101 Switching Protocols - Server switching to different protocol
49
*/
50
val SwitchingProtocols: Status.SwitchingProtocols.type
51
52
/**
53
* 102 Processing - Server received request, still processing
54
*/
55
val Processing: Status.Processing.type
56
57
/**
58
* 103 Early Hints - Early metadata before final response
59
*/
60
val EarlyHints: Status.EarlyHints.type
61
```
62
63
**Usage Examples:**
64
65
```scala
66
val routes = HttpRoutes.of[IO] {
67
case GET -> Root / "status" =>
68
Continue()
69
70
case GET -> Root / "upgrade" =>
71
SwitchingProtocols()
72
}
73
```
74
75
### 2xx Success Responses
76
77
Success status codes indicating successful request processing.
78
79
```scala { .api }
80
/**
81
* 200 OK - Standard successful response
82
*/
83
val Ok: Status.Ok.type
84
85
/**
86
* 201 Created - Resource successfully created
87
*/
88
val Created: Status.Created.type
89
90
/**
91
* 202 Accepted - Request accepted for processing
92
*/
93
val Accepted: Status.Accepted.type
94
95
/**
96
* 203 Non-Authoritative Information - Successful but from cache/proxy
97
*/
98
val NonAuthoritativeInformation: Status.NonAuthoritativeInformation.type
99
100
/**
101
* 204 No Content - Successful request with no response body
102
*/
103
val NoContent: Status.NoContent.type
104
105
/**
106
* 205 Reset Content - Successful request, client should reset form
107
*/
108
val ResetContent: Status.ResetContent.type
109
110
/**
111
* 206 Partial Content - Partial resource delivered
112
*/
113
val PartialContent: Status.PartialContent.type
114
115
/**
116
* 207 Multi-Status - Multiple status codes for WebDAV
117
*/
118
val MultiStatus: Status.MultiStatus.type
119
120
/**
121
* 208 Already Reported - WebDAV already reported
122
*/
123
val AlreadyReported: Status.AlreadyReported.type
124
125
/**
126
* 226 IM Used - Instance manipulations applied
127
*/
128
val IMUsed: Status.IMUsed.type
129
```
130
131
**Usage Examples:**
132
133
```scala
134
val routes = HttpRoutes.of[IO] {
135
// Simple responses
136
case GET -> Root / "hello" =>
137
Ok("Hello, World!")
138
139
case POST -> Root / "users" =>
140
Created("User created successfully")
141
142
// With custom headers
143
case GET -> Root / "data" =>
144
Ok("response body", Header.Raw(ci"X-Custom-Header", "custom-value"))
145
146
// Empty responses
147
case DELETE -> Root / "users" / IntVar(id) =>
148
NoContent()
149
150
// JSON responses
151
case GET -> Root / "users" / IntVar(id) =>
152
Ok(User(id, "John Doe").asJson)
153
}
154
```
155
156
### 3xx Redirection Responses
157
158
Redirection status codes requiring additional client action.
159
160
```scala { .api }
161
/**
162
* 300 Multiple Choices - Multiple representation options
163
*/
164
val MultipleChoices: Status.MultipleChoices.type
165
166
/**
167
* 301 Moved Permanently - Resource permanently moved
168
*/
169
val MovedPermanently: Status.MovedPermanently.type
170
171
/**
172
* 302 Found - Resource temporarily moved
173
*/
174
val Found: Status.Found.type
175
176
/**
177
* 303 See Other - Response available at different URI
178
*/
179
val SeeOther: Status.SeeOther.type
180
181
/**
182
* 304 Not Modified - Resource not modified since last request
183
*/
184
val NotModified: Status.NotModified.type
185
186
/**
187
* 307 Temporary Redirect - Temporary redirect preserving method
188
*/
189
val TemporaryRedirect: Status.TemporaryRedirect.type
190
191
/**
192
* 308 Permanent Redirect - Permanent redirect preserving method
193
*/
194
val PermanentRedirect: Status.PermanentRedirect.type
195
```
196
197
**Usage Examples:**
198
199
```scala
200
import org.http4s.headers.Location
201
import org.http4s.Uri
202
203
val routes = HttpRoutes.of[IO] {
204
// Redirects with Location header
205
case GET -> Root / "old-path" =>
206
MovedPermanently(Location(Uri.unsafeFromString("/new-path")))
207
208
case POST -> Root / "login" =>
209
SeeOther(Location(Uri.unsafeFromString("/dashboard")))
210
211
// Conditional responses
212
case req @ GET -> Root / "resource" =>
213
req.headers.get[`If-Modified-Since`] match {
214
case Some(since) if !isModifiedSince(since) => NotModified()
215
case _ => Ok("Resource content")
216
}
217
}
218
```
219
220
### 4xx Client Error Responses
221
222
Client error status codes indicating issues with client requests.
223
224
```scala { .api }
225
/**
226
* 400 Bad Request - Invalid request syntax
227
*/
228
val BadRequest: Status.BadRequest.type
229
230
/**
231
* 401 Unauthorized - Authentication required
232
*/
233
val Unauthorized: Status.Unauthorized.type
234
235
/**
236
* 402 Payment Required - Payment required (reserved)
237
*/
238
val PaymentRequired: Status.PaymentRequired.type
239
240
/**
241
* 403 Forbidden - Access denied
242
*/
243
val Forbidden: Status.Forbidden.type
244
245
/**
246
* 404 Not Found - Resource not found
247
*/
248
val NotFound: Status.NotFound.type
249
250
/**
251
* 405 Method Not Allowed - HTTP method not supported
252
*/
253
val MethodNotAllowed: Status.MethodNotAllowed.type
254
255
/**
256
* 406 Not Acceptable - Response format not acceptable
257
*/
258
val NotAcceptable: Status.NotAcceptable.type
259
260
/**
261
* 407 Proxy Authentication Required - Proxy authentication needed
262
*/
263
val ProxyAuthenticationRequired: Status.ProxyAuthenticationRequired.type
264
265
/**
266
* 408 Request Timeout - Request timeout
267
*/
268
val RequestTimeout: Status.RequestTimeout.type
269
270
/**
271
* 409 Conflict - Request conflicts with current state
272
*/
273
val Conflict: Status.Conflict.type
274
275
/**
276
* 410 Gone - Resource permanently gone
277
*/
278
val Gone: Status.Gone.type
279
280
/**
281
* 411 Length Required - Content-Length header required
282
*/
283
val LengthRequired: Status.LengthRequired.type
284
285
/**
286
* 412 Precondition Failed - Precondition in headers failed
287
*/
288
val PreconditionFailed: Status.PreconditionFailed.type
289
290
/**
291
* 413 Payload Too Large - Request entity too large
292
*/
293
val PayloadTooLarge: Status.PayloadTooLarge.type
294
295
/**
296
* 414 URI Too Long - Request URI too long
297
*/
298
val UriTooLong: Status.UriTooLong.type
299
300
/**
301
* 415 Unsupported Media Type - Media type not supported
302
*/
303
val UnsupportedMediaType: Status.UnsupportedMediaType.type
304
305
/**
306
* 416 Range Not Satisfiable - Range header cannot be satisfied
307
*/
308
val RangeNotSatisfiable: Status.RangeNotSatisfiable.type
309
310
/**
311
* 417 Expectation Failed - Expect header cannot be satisfied
312
*/
313
val ExpectationFailed: Status.ExpectationFailed.type
314
315
/**
316
* 421 Misdirected Request - Request directed to wrong server
317
*/
318
val MisdirectedRequest: Status.MisdirectedRequest.type
319
320
/**
321
* 422 Unprocessable Entity - Request syntactically correct but semantically invalid
322
*/
323
val UnprocessableEntity: Status.UnprocessableEntity.type
324
325
/**
326
* 423 Locked - Resource locked
327
*/
328
val Locked: Status.Locked.type
329
330
/**
331
* 424 Failed Dependency - Request failed due to dependency failure
332
*/
333
val FailedDependency: Status.FailedDependency.type
334
335
/**
336
* 425 Too Early - Request sent too early
337
*/
338
val TooEarly: Status.TooEarly.type
339
340
/**
341
* 426 Upgrade Required - Client must upgrade protocol
342
*/
343
val UpgradeRequired: Status.UpgradeRequired.type
344
345
/**
346
* 428 Precondition Required - Precondition header required
347
*/
348
val PreconditionRequired: Status.PreconditionRequired.type
349
350
/**
351
* 429 Too Many Requests - Rate limit exceeded
352
*/
353
val TooManyRequests: Status.TooManyRequests.type
354
355
/**
356
* 431 Request Header Fields Too Large - Headers too large
357
*/
358
val RequestHeaderFieldsTooLarge: Status.RequestHeaderFieldsTooLarge.type
359
360
/**
361
* 451 Unavailable For Legal Reasons - Content blocked for legal reasons
362
*/
363
val UnavailableForLegalReasons: Status.UnavailableForLegalReasons.type
364
```
365
366
**Usage Examples:**
367
368
```scala
369
val routes = HttpRoutes.of[IO] {
370
// Error responses with messages
371
case POST -> Root / "users" =>
372
for {
373
user <- req.as[User].handleErrorWith(_ => BadRequest("Invalid user data").pure)
374
result <- createUser(user).handleErrorWith(_ => Conflict("User already exists").pure)
375
} yield result
376
377
// Authentication errors
378
case GET -> Root / "protected" =>
379
Unauthorized("""WWW-Authenticate: Bearer realm="api"""")
380
381
// Not found
382
case GET -> Root / "users" / IntVar(id) =>
383
findUser(id).flatMap {
384
case Some(user) => Ok(user.asJson)
385
case None => NotFound(s"User $id not found")
386
}
387
388
// Validation errors
389
case req @ POST -> Root / "validate" =>
390
req.as[Data].attempt.flatMap {
391
case Right(data) => Ok("Valid data")
392
case Left(_) => UnprocessableEntity("Invalid data format")
393
}
394
}
395
```
396
397
### 5xx Server Error Responses
398
399
Server error status codes indicating server-side issues.
400
401
```scala { .api }
402
/**
403
* 500 Internal Server Error - Generic server error
404
*/
405
val InternalServerError: Status.InternalServerError.type
406
407
/**
408
* 501 Not Implemented - Server doesn't support functionality
409
*/
410
val NotImplemented: Status.NotImplemented.type
411
412
/**
413
* 502 Bad Gateway - Invalid response from upstream server
414
*/
415
val BadGateway: Status.BadGateway.type
416
417
/**
418
* 503 Service Unavailable - Server temporarily unavailable
419
*/
420
val ServiceUnavailable: Status.ServiceUnavailable.type
421
422
/**
423
* 504 Gateway Timeout - Upstream server timeout
424
*/
425
val GatewayTimeout: Status.GatewayTimeout.type
426
427
/**
428
* 505 HTTP Version Not Supported - HTTP version not supported
429
*/
430
val HttpVersionNotSupported: Status.HttpVersionNotSupported.type
431
432
/**
433
* 506 Variant Also Negotiates - Content negotiation error
434
*/
435
val VariantAlsoNegotiates: Status.VariantAlsoNegotiates.type
436
437
/**
438
* 507 Insufficient Storage - Server out of storage
439
*/
440
val InsufficientStorage: Status.InsufficientStorage.type
441
442
/**
443
* 508 Loop Detected - Infinite loop detected
444
*/
445
val LoopDetected: Status.LoopDetected.type
446
447
/**
448
* 510 Not Extended - Further extensions required
449
*/
450
val NotExtended: Status.NotExtended.type
451
452
/**
453
* 511 Network Authentication Required - Network authentication required
454
*/
455
val NetworkAuthenticationRequired: Status.NetworkAuthenticationRequired.type
456
```
457
458
**Usage Examples:**
459
460
```scala
461
val routes = HttpRoutes.of[IO] {
462
case GET -> Root / "data" =>
463
fetchData().handleErrorWith { error =>
464
error match {
465
case _: DatabaseConnectionException => ServiceUnavailable("Database temporarily unavailable")
466
case _: TimeoutException => GatewayTimeout("Request timeout")
467
case _ => InternalServerError("An unexpected error occurred")
468
}
469
}
470
471
case GET -> Root / "unsupported" =>
472
NotImplemented("This endpoint is not yet implemented")
473
}
474
```
475
476
### Specialized Response Generators
477
478
Some status codes have specialized response generators with additional requirements:
479
480
#### WWW-Authenticate Response Generator (401)
481
482
```scala { .api }
483
/**
484
* 401 responses must include WWW-Authenticate header
485
*/
486
trait WwwAuthenticateResponseGenerator[F[_], G[_]] extends ResponseGenerator {
487
def apply(authenticate: `WWW-Authenticate`, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
488
def apply[A](authenticate: `WWW-Authenticate`, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
489
}
490
```
491
492
#### Allow Response Generator (405)
493
494
```scala { .api }
495
/**
496
* 405 responses must include Allow header
497
*/
498
trait AllowResponseGenerator[F[_], G[_]] extends ResponseGenerator {
499
def apply(allow: Allow, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
500
def apply[A](allow: Allow, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
501
}
502
```
503
504
#### Proxy-Authenticate Response Generator (407)
505
506
```scala { .api }
507
/**
508
* 407 responses must include Proxy-Authenticate header
509
*/
510
trait ProxyAuthenticateResponseGenerator[F[_], G[_]] extends ResponseGenerator {
511
def apply(authenticate: `Proxy-Authenticate`, headers: Header.ToRaw*)(implicit F: Applicative[F]): F[Response[G]]
512
def apply[A](authenticate: `Proxy-Authenticate`, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
513
}
514
```
515
516
#### Location Response Generator (3xx)
517
518
```scala { .api }
519
/**
520
* Redirect responses should include Location header
521
*/
522
trait LocationResponseGenerator[F[_], G[_]] extends EntityResponseGenerator[F, G] {
523
def apply(location: Location)(implicit F: Applicative[F]): F[Response[G]]
524
def apply[A](location: Location, body: A, headers: Header.ToRaw*)(implicit F: Applicative[F], w: EntityEncoder[G, A]): F[Response[G]]
525
}
526
```