# Yohoho - A take-home assignment for HolidayPirates This is my take on the take-home assignment I was given for the Backend Engineer position at HolidayPirates. The goal is to implement a very small REST API in Clojure. That API provides two endpoints : * `POST /items`: creates an item * `GET /items`: returns the list of items This was a great opportunity for me to learn more about Clojure. Whatever the result of the recruitment process, it was really fun working on this assignment! ## Running and testing This project uses Leiningen. Assuming you already have Leiningen installed, running the API should be as easy as cloning the repo and issuing `lein run`. By default, the server listens on port 3000. To explore the API using Swagger UI, you can head to . To run the unit and integration tests: `lein test` ## Structure The source code lies in `/src/yohoho` and is structured as follows: * `app.clj`: ring app and main entrypoint * `config.clj`: configuration (database and HTTP server) * `db.clj`: functions that directly interact with the database * `handlers.clj`: route handlers * `helpers.clj`: the infamous file that holds function that are useful but couldn't fit anywhere else * `routes.clj`: API routes * `schemas.cls`: Malli schemas used for validation ## Library/tools choices Since this is just a small assignment, I sticked to simple and proven tools: * SQLite for the database (easy to setup, easy to use with `next.jdbc`) * Leiningen for managing the project (it seems to be getting outdated in favor of Clojure CLI Tools, but is still widely used and I could find more documentation) * Libraries : `reitit` for route handling + `malli` for validation + `muuntaja` for handling json content. Thanks for the suggestions in the assignment! I chose `reitit` because it seemed more complete/integrated than Compojure. However, in the context of this simple assignment, Compojure may have been a better choice (would have probably been easier to learn) * Web server: `jetty` (seems to be pretty standard) ## Comments Respect of the assignment: * The API features the `POST /items` and `GET /items` endpoints * `POST /items` accepts a JSON payload, validates it and returns HTTP 400 is validation fails * `POST /items` persists data in SQLite * `GET /items` returns the list of all items as JSON. * The structure of the items is respected, although it just includes the bare minimum (id, name, email). * A data validation library is used (`malli`) * Unit tests were written (although generated with the help of Claude Code) * Integration tests were written (although generated with the help of Claude Code too) * The API is documented (OpenAPI `/openapi.json` + Swagger UI at `/doc`) Divergences from the assignment: * The `POST /items` do not take the id in the input. Instead, I prefer to let the database generate a new id on its own. * As a consequence, the `id` of an item is an integer, not a string. SQLite can autogenerate/autoincrement only on a integer id. What was not part of the assignment: * `GET /ahoy` health check endpoint. As you may guess, this was my very first endpoint as I was learning how to use `reitit`. I decided to keep it, because a health check endpoint cannot hurt. * `GET /item/:id` was included. This was quite easy to add, and also makes sense since the `POST /items` endpoint follows standard practice of returning a `Location` header for the newly created item. Security: * SQL injections: we should be ok since we are not using raw SQL statements in `db.clj` (`next.jdbc` will use parametrized statements under the hood) * `POST /items` input has validation * See the "Desirable improvement" section for some missing security features ## Desirable improvements This project is voluntarily kept simple. If it were to get deployed in production, the following should be dealt with, in no particular order: * `GET /items`: add pagination * Security: Add rate limiting * Security: Add authentication/authorization * Security: Add and configure CORS middleware * Add logging * TLS: it's fine that the API is accessible only through http, but it should be deployed behind a reverse proxy to add TLS/https support. This is quite easy to do with Apache, Nginx or Caddy. * Database: use a migration system. This was definitely unnecessary for this assignment, but should be used for a more serious project. Migratus seems to be the de-facto library for this. * Database: switch to a more serious database (PostgreSQL comes to mind), add connection pooling, use transactions. * Add metrics (request rate, response time, database connection pool stats, etc.) and monitoring (eg. using Datadog). * Configuration: add the ability to override deffault configuration using a dotenv file and/or environment variables. * Add a supervisor to ensure the API keeps running (could be as easy as using systemd, or as complex as deploying with kubernetes) ## Documentation links The following links proved more than useful when working on this assignment: * * * * * * * * *