···1111 wisp.configure_logger()
1212 let secret_key_base = wisp.random_string(64)
13131414- // TODO: document
1414+ // A database creation is created here, when the program starts.
1515+ // This connection is used by all requests.
1516 use db <- tiny_database.with_connection(data_directory)
16171717- // TODO: document
1818+ // A context is constructed to hold the database connection.
1819 let context = web.Context(db: db)
19202020- // TODO: document
2121+ // The handle_request function is partially applied with the context to make
2222+ // the request handler function that only takes a request.
2323+ let handler = router.handle_request(_, context)
2424+2125 let assert Ok(_) =
2222- router.handle_request(_, context)
2626+ handler
2327 |> wisp.mist_handler(secret_key_base)
2428 |> mist.new
2529 |> mist.port(8000)
+6
examples/5-using-a-database/src/app/router.gleam
···55pub fn handle_request(req: Request, ctx: Context) -> Response {
66 use req <- web.middleware(req)
7788+ // A new `app/web/people` module now contains the handlers and other functions
99+ // relating to the People feature of the application.
1010+ //
1111+ // The router module now only deals with routing, and dispatches to the
1212+ // feature modules for handling requests.
1313+ //
814 case wisp.path_segments(req) {
915 ["people"] -> people.all(req, ctx)
1016 ["people", id] -> people.one(req, ctx, id)
+7
examples/5-using-a-database/src/app/web.gleam
···11import wisp
22import tiny_database
3344+// A new Context type, which holds any additional data that the request handlers
55+// need in addition to the request.
66+//
77+// Here it is holding a database connection, but it could hold anything else
88+// such as API keys, IO performing functions (so they can be swapped out in
99+// tests for mock implementations), configuration, and so on.
1010+//
411pub type Context {
512 Context(db: tiny_database.Connection)
613}
···77import tiny_database
88import wisp.{Request, Response}
991010+// This request handler is used for requests to `/people`.
1111+//
1012pub fn all(req: Request, ctx: Context) -> Response {
1313+ // Dispatch to the appropriate handler based on the HTTP method.
1114 case req.method {
1215 Get -> list_people(ctx)
1316 Post -> create_person(req, ctx)
···1518 }
1619}
17202121+// This request handler is used for requests to `/people/:id`.
2222+//
1823pub fn one(req: Request, ctx: Context, id: String) -> Response {
2424+ // Dispatch to the appropriate handler based on the HTTP method.
1925 case req.method {
2026 Get -> read_person(ctx, id)
2127 _ -> wisp.method_not_allowed([Get])
···2632 Person(name: String, favourite_colour: String)
2733}
28343535+// This handler returns a list of all the people in the database, in JSON
3636+// format.
3737+//
2938pub fn list_people(ctx: Context) -> Response {
3039 let result = {
4040+ // Get all the ids from the database.
3141 use ids <- try(tiny_database.list(ctx.db))
3232- let object =
3333- json.object([
3434- #(
3535- "people",
3636- json.array(ids, fn(id) { json.object([#("id", json.string(id))]) }),
3737- ),
3838- ])
3939- Ok(json.to_string_builder(object))
4242+4343+ // Convert the ids into a JSON array of objects.
4444+ Ok(json.to_string_builder(json.object([
4545+ #(
4646+ "people",
4747+ json.array(ids, fn(id) { json.object([#("id", json.string(id))]) }),
4848+ ),
4949+ ])))
4050 }
41514252 case result {
5353+ // When everything goes well we return a 200 response with the JSON.
4354 Ok(json) -> wisp.json_response(json, 200)
5555+5656+ // In a later example we will see how to return specific errors to the user
5757+ // depending on what went wrong. For now we will just return a 500 error.
4458 Error(Nil) -> wisp.internal_server_error()
4559 }
4660}
47614862pub fn create_person(req: Request, ctx: Context) -> Response {
6363+ // Read the JSON from the request body.
4964 use json <- wisp.require_json(req)
50655166 let result = {
6767+ // Decode the JSON into a Person record.
5268 use person <- try(decode_person(json))
6969+7070+ // Save the person to the database.
5371 use id <- try(save_to_database(ctx.db, person))
5454- let object = json.object([#("id", json.string(id))])
5555- Ok(json.to_string_builder(object))
7272+7373+ // Construct a JSON payload with the id of the newly created person.
7474+ Ok(json.to_string_builder(json.object([#("id", json.string(id))])))
5675 }
57767777+ // Return an appropriate response depending on whether everything went well or
7878+ // if there was an error.
5879 case result {
5980 Ok(json) -> wisp.json_response(json, 201)
6081 Error(Nil) -> wisp.unprocessable_entity()
···63846485pub fn read_person(ctx: Context, id: String) -> Response {
6586 let result = {
8787+ // Read the person with the given id from the database.
6688 use person <- try(read_from_database(ctx.db, id))
6767- let object =
6868- json.object([
6969- #("id", json.string(id)),
7070- #("name", json.string(person.name)),
7171- #("favourite-colour", json.string(person.favourite_colour)),
7272- ])
7373- Ok(json.to_string_builder(object))
8989+9090+ // Construct a JSON payload with the person's details.
9191+ Ok(json.to_string_builder(json.object([
9292+ #("id", json.string(id)),
9393+ #("name", json.string(person.name)),
9494+ #("favourite-colour", json.string(person.favourite_colour)),
9595+ ])))
7496 }
75979898+ // Return an appropriate response.
7699 case result {
7777- Ok(json) -> wisp.json_response(json, 201)
100100+ Ok(json) -> wisp.json_response(json, 200)
78101 Error(Nil) -> wisp.not_found()
79102 }
80103}
···94117 |> result.nil_error
95118}
961199797-// TODO: document
120120+/// Save a person to the database and return the id of the newly created record.
98121pub fn save_to_database(
99122 db: tiny_database.Connection,
100123 person: Person,
101124) -> Result(String, Nil) {
125125+ // In a real application you might use a database client with some SQL here.
126126+ // Instead we create a simple map and save that.
102127 let data =
103128 map.from_list([
104129 #("name", person.name),
···107132 tiny_database.insert(db, data)
108133}
109134110110-// TODO: document
111135pub fn read_from_database(
112136 db: tiny_database.Connection,
113137 id: String,
114138) -> Result(Person, Nil) {
139139+ // In a real application you might use a database client with some SQL here.
115140 use data <- try(tiny_database.read(db, id))
116141 use name <- try(map.get(data, "name"))
117142 use favourite_colour <- try(map.get(data, "favourite-colour"))