A simple but comprehensive REST API for a tic-tac-toe game
Get the code first:
git clone https://github.com/lutostag/tic-tac-toe
cd tic-tac-toe
Using docker:
docker-compose up
If you prefer a non-docker workflow, please be in a virtual environment running Python3.7:
make deps dev
In your browser, navigate to:
- http://localhost/redoc for documentation
- http://localhost/docs for a swagger ui that you can use to interact with REST-API via your browser.
(note that if you are not using docker, the dev server is typically listening on port :8000)
This project implements a REST API for playing tic-tac-toe
All game state is saved and can be viewed/returned to whenever the players want to.
Game state transitions are validated to prevent clients from cheating and operating out of turn.
Players can enter their names, the first player to join is 'x', the second is 'o'.
Python 3 -- language that has good support for backend servers
Backend:
- FastAPI -- a relatively new framework, taking it for a spin for the first time. I agree with a lot of the sentiments, and is asyncio friendly in implementation.
- Pydantic -- a full featured data modelling framework, very helpful for validation and data wrangling into and out of ORMs, first time using this one as well.
- SQLAlchemy for talking with the database -- in this case using SQLite for local development and PostgreSQL or any other SQL backend. The challenge may be better suited to a non-SQL database, but with the validation of progression, using transactions was important, and given there were already lots of new tools to learn, sticking with these tools made it easier to spin up, and introspect for development.
Testing:
- Requests -- a great http client (only missing asyncio for speed), but great for flexible testing
- Pytest -- really useful for dealing with test parameterization
Deployment:
- Docker -- using docker-compose to ensure that the server can be built/run on different machines without too much hassle
As there only one type of data (games) exposed by this API, making a good representation of it is important.
The game objects use the following format that is exposed to the clients via the endpoints:
{
"players": [
"one",
"two"
],
"state": [
[null, null, 0],
[null, null, null],
[null, null, 1]
]
}
On every update of the state of a game validation is performed to ensure that only valid tic-tac-toe moves occur. This ensures that clients cannot "cheat" by sending multiple moves or skip turns.
The server complies with the JSON API specification, in this case exposing an OpenAPI (formerly know as swagger -- it switched names in the v2 -> v3 transition).
-
GET /api/games
: Return a list of the Games known to the server, as JSON. -
POST /api/games
: Create a newGame
, assigning it an ID and returning the newly createdGame
. -
GET /api/games/<id>
: Retrieve aGame
by its ID, returning a404
status code if no game with that ID exists. -
POST /api/games/<id>
: Update theGame
with the given ID, replacing its data with the newlyPOST
ed data.
Since the openapi definition is exposed you can create a client by using tooling such as:
- https://github.com/OpenAPITools/openapi-generator
- https://github.com/anttiviljami/openapi-client-axios
Most of the common workflows, (development/hot-reloading server, testing, running) are usable via make
Setup your workspace:
- python3.7
- be in a virtualenv
make deps # to install depenencies
make dev # to start a hot-reloading dev server
Typical workflow:
- create a new branch on git
- make any desired changes
- add tests to
tests/
make fmt # to reformat your code using black
make test # to test your code is working
- push and submit the code as a PR -- note the upstream repo where the idea came from is https://github.com/ContinuumIO/tic-tac-toe-challenge
Notes:
- you can point the tests at a different server by setting the
TICTACTOE_SERVER
environment variable, e.g.TICTACTOE_SERVER=http://localhost make test
this is typically helpful to test against the docker container or other already deployed servers.
- Setup CI
- Utilize redis for only holding on to in-progress games with a 5min TTL, then games can optionally be saved to the backend database if desired by clients.
- Alert clients on turns via websockets, so clients do not have to poll.
- A terminal tui client, perhaps using urwid