TrueLayer Pokemon Challenge
The challenge has been solved using Rust and tested with stable Rust 1.52.
Building and running
The application can be easily run using the provided Dockerfile.
Install Docker as described here, and then run from the root of the project:
docker build -t truelayer-pokemon-challenge .
docker run --rm -ti -p 8080:8080 truelayer-pokemon-challenge
The server will be available at http://localhost:8080
.
$ curl http://localhost:8080/pokemon/pikachu
{"name":"pikachu","description":"At which hour several of these pokémon gather, [...]"}
Running the tests
Tests can also be run using the provided Dockerfile.
docker build -t truelayer-pokemon-challenge-test -f Dockerfile.test .
docker run --rm -ti truelayer-pokemon-challenge-test
General description
The API is implemented with Rust using warp
.
When a request for a new Pokemon description comes in, the following happens:
- The Pokemon name is validated using the
PokeAPI
and its description retrieved. - The PokeAPI returns more than one description, so we select the first one available in the English language.
- We send a request to the
Shakespeare Translator
to translate the description into Shakespearean language. - The result is cached for further requests in an in-memory LRU cache.
Exposed routes
-
GET /pokemon/{string}
: Returns the translated description of the Pokemon with the given name. -
GET /health
: Healthcheck endpoint used to check whether the application is alive or not. -
GET /metrics
: Endpoint to scrape Prometheus metrics generated by the application.
Configuration
The configuration of the application can be tweaked using the following environment variables
PORT
: Port to bind the server to.RUST_LOG
: Logging configuration. Look here for documentation on the format.SHAKESPEARE_TRANSLATOR_ENDPOINT
: Base url of the Shakespeare Translator API.POKEAPI_ENDPOINT
: Base url of the Pokemon API.POKEAPI_CACHE_SIZE
: Number of Pokemons to keep in the LRU cache.
Areas of improvement
Due to the short time of the challenge, a few important aspects have been glossed over.
Here are some possible areas of improvement:
-
Better error handling
- The errors have been handled using
anyhow
, while a proper application should define its internalError
types for proper pattern matching and error handling. - Better handling of rate limit errors for Shakespeare API, which has a hard limit of 5 calls/hour. When a rate limit error is encountered, it is just logs the error and returns an HTTP 500 to the client. It would be better to properly handle the situation and return an HTTP 429.
- The errors have been handled using
-
Reliability of the calls to external services
- Temporary errors could be retried.
- Limit the maximum number of concurrently in-flight requests to external services. Right now, if two requests for the same uncached Pokemon arrive at the same time, two duplicate requests are sent to the external APIs. Those requests could be deduped in order to improve performance.
-
Caching
- In this scenario is very important to cache the translated descriptions in order to reduce the number of calls to external services and improve performance. The data handled by this application is static and readonly, which means that it is very cache-friendly. Right now, a very simple in-memory LRU cache implements a memory-bounded cache which could be swapped for a more proper solution like Redis.
-
HTTP metrics
- Observability is paramount in a complex distributed system. The application exposes metrics about the number of requests made to the external APIs and the number of cache hits. A true production-ready application would also expose metrics about the timings of the response and other statistics about the process.