Here you may find TODO-list backend application implemented with Servant, Haskell library for web development.
It has basic CRUD operations, responds with JSON only, uses Persistent as a storage interface, and additionally implements a simple custom logger middleware.
The main goal of this project is to provide a basic example of implementing backend with Servant library, so to make life a bit easier for (future) Haskell web developers.
This code complements the talk "How to Choose a Haskell Web Framework" I presented at Haskell eXchange 2022.
In this talk, I show how some of Haskell web tools approach web development. In particular, there are 3 of them — Servant, Yesod and IHP.
There are also corresponding Yesod and IHP TODO-list implementations.
Check out the presentation slides and the talk recording, and contact me if you have any questions 🙂
As application has only basic CRUD operations, here is the API
GET /api/task
— get all tasksPOST /api/task
— create new taskGET /api/task/{id}
— get existing task byid
PUT /api/task/{id}
— update existing task byid
DELETE /api/task/{id}
— delete existing task byid
We use curl to demonstrate interaction with API
> curl -X GET http://localhost:4000/api/task
[]
> curl -X POST http://localhost:4000/api/task -H "Content-Type: application/json" -d '{"content": "wake up"}'
{"content":"wake up","id":1}
> curl -X POST http://localhost:4000/api/task -H "Content-Type: application/json" -d '{"content": "drink coffee"}'
{"content":"drink coffee","id":2}
> curl -X PUT http://localhost:4000/api/task/2 -H "Content-Type: application/json" -d '{"content": "drink mooore coffee"}'
{"content":"drink mooore coffee","id":2}
> curl -X GET http://localhost:4000/api/task
[{"content":"wake up","id":1},{"content":"drink mooore coffee","id":2}]
> curl -X GET http://localhost:4000/api/task/2
{"content":"drink mooore coffee","id":2}
> curl -X GET http://localhost:4000/api/task/5
> curl -X DELETE http://localhost:4000/api/task/1
{"content":"wake up","id":1}
> curl -X GET http://localhost:4000/api/task
[{"content":"drink mooore coffee","id":2}]
Simple logger here just extracts request’s URL path and logs it with "Info" verbosity level.
For example,
[Info] url-path=api/task/5
[Info] url-path=api/task/
[Info] url-path=api/task
[Info] url-path=api/task/8/
[Info] url-path=api/task/3
[Info] url-path=/
[Info] url-path=api/task
Firstly, you need to configure a connection string to connect to your database. You may refer to the PostgreSQL docs for this.
In this example, the following connection string is used
connectionStr = "host=localhost dbname=todolist-servant user=postgres password=postgres port=5432"
Which means that the local todolist-servant
postgres database is accessed via 5432
port.
Make sure that user and database exist before running the app.
Then, run the app
> stack run
Migrating: CREATe TABLE "task"("id" SERIAL8 PRIMARY KEY UNIQUE,"content" VARCHAR NOT NULL)
[Debug#SQL] CREATe TABLE "task"("id" SERIAL8 PRIMARY KEY UNIQUE,"content" VARCHAR NOT NULL); []
...
Hope you'll find it helpful 💙