howdy/service
Services hold application logic and return plain results. This module turns those results into HTTP responses so controllers stay lean.
fn by_id(ctx: Context) {
use id <- param.int(ctx, "id")
user_service.find(id)
|> service.respond(ctx, user.to_json)
}
Types
The ways a service call can fail. Each variant maps to a status code.
pub type Error {
NotFound(String)
Invalid(String)
Conflict(String)
Unauthorized
Forbidden
UnsupportedMediaType(String)
Internal(String)
Validation(List(FieldError))
TooManyRequests(retry_after_seconds: Int)
Coded(code: String, error: Error)
}
Constructors
-
NotFound(String)- The message names what was not found.
-
Invalid(String)- The message explains what was wrong with the input.
-
Conflict(String)- The message explains the conflict.
-
Unauthorized -
Forbidden -
UnsupportedMediaType(String)- The message names the content type that was expected. Usually
produced by
howdy/form.
- The message names the content type that was expected. Usually
produced by
-
Internal(String)- The message is logged and never sent to the client.
-
Validation(List(FieldError))- Input was well formed but failed validation. Usually produced by
howdy/validate, but a service can return it too, for example for a duplicate email.
- Input was well formed but failed validation. Usually produced by
-
TooManyRequests(retry_after_seconds: Int)429 with a
retry-afterheader. Usually produced byhowdy/rate_limit, but a service can return it too, for example for password resets. -
Coded(code: String, error: Error)Another error with a stable code for clients, which answers as
errordoes withcodein its body. A client that shows errors in other languages translates by code, since the message is English:Coded("email_taken", Conflict("an account already exists")).uncodedgives the error inside, for matching on its kind.
One problem with one field. The same type as validate.FieldError.
pub type FieldError {
FieldError(field: String, problem: problem.Problem)
}
Constructors
-
FieldError(field: String, problem: problem.Problem)
Values
pub fn code(error: Error) -> String
The code sent to the client for an error: a Coded error’s own, or one
for its kind, such as not_found or too_many_requests.
pub fn created(
result: Result(a, Error),
ctx: context.Context(guarded),
encode: fn(a) -> json.Json,
) -> response.Response(content.Content)
Like respond but Ok becomes 201.
pub fn error_response(
ctx: context.Context(guarded),
error: Error,
) -> response.Response(content.Content)
Build the response for an error. Used by respond and by the extractors
in howdy/param and howdy/body so every error has the same shape.
pub fn no_content(
result: Result(Nil, Error),
ctx: context.Context(guarded),
) -> response.Response(content.Content)
Ok becomes 204 with no body. Useful for deletes.
pub fn respond(
result: Result(a, Error),
ctx: context.Context(guarded),
encode: fn(a) -> json.Json,
) -> response.Response(content.Content)
Ok becomes 200 with the encoded value. Error becomes the matching
status with a JSON body of {"error": "...", "code": "..."}.