A game leaderboard implemented in Golang, Redis and MongoDB. Includes general and country based ranking with Redis Sorted Sets, MongoDB as persistent database and password hash encryption with JWT authentication.
https://arcane-beach-01523.herokuapp.com/
The app is deployed to Heroku using Heroku Redis, and a Mongodb cluster on Mongodb Atlas. All free tier.
To run the application locally you need;
- Golang
- MongoDB
- Redis
All these tools above are configured to run on their local default configurations.
Once you completed setting them up, please run god mod download
to download Go dependencies. You can see the list of dependencies in go.mod
. Please don't forget to add the required environment variables.
If you prefer you can run the application in a docker container by typing;
docker-compose -f docker-compose.yml up
Various endpoint that return leaderboard in different ways. All endpoints under Leaderboard requires a token to be in the Authorization header
Returns the entire leaderboard of all users. This request takes a lot of time. Heroku as a timeout of 30 seconds. In large collections of data this will return a 503 - Request Timeout. It's better to use paging with the range endpoint considering both time and response payload.
[
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "0ioYXaaaaaa3V",
"country": "NZ",
"points": 10000,
"rank": 1,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff1a3bb",
"display_name": "DOMsOa",
"country": "VE",
"points": 10000,
"rank": 2,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18418",
"display_name": "UOMNO",
"country": "NI",
"points": 10000,
"rank": 3,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18d37",
"display_name": "5gHkycaaaaM2rRF",
"country": "VI",
"points": 9999,
"rank": 4,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff1a2b9",
"display_name": "kMiJobaaaayAucg",
"country": "MG",
"points": 9998,
"rank": 5,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff1a236",
"display_name": "o16LIdaaaae",
"country": "UA",
"points": 9998,
"rank": 6,
"last_score_timestamp": ""
}
]
Returns the leaderboard up to the number e.g. 4
[
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "0ioYXaaaaaa3V",
"country": "NZ",
"points": 10000,
"rank": 1,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff1a3bb",
"display_name": "DOMsOa",
"country": "VE",
"points": 10000,
"rank": 2,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18418",
"display_name": "UOMNO",
"country": "NI",
"points": 10000,
"rank": 3,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18d37",
"display_name": "5gHkycaaaaM2rRF",
"country": "VI",
"points": 9999,
"rank": 4,
"last_score_timestamp": ""
},
]
Returns the leaderboard of a country e.g. VI
[
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "0ioYXaaaaaa3V",
"country": "VI",
"points": 10000,
"rank": 1,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff1a3bb",
"display_name": "DOMsOa",
"country": "VI",
"points": 9999,
"rank": 2,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18418",
"display_name": "UOMNO",
"country": "VI",
"points": 9999,
"rank": 3,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18d37",
"display_name": "5gHkycaaaaM2rRF",
"country": "VI",
"points": 9998,
"rank": 4,
"last_score_timestamp": ""
},
]
Returns the leaderboard of a country e.g. VI with a limit
[
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "0ioYXaaaaaa3V",
"country": "VI",
"points": 10000,
"rank": 1,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff1a3bb",
"display_name": "DOMsOa",
"country": "VI",
"points": 9999,
"rank": 2,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18418",
"display_name": "UOMNO",
"country": "VI",
"points": 9999,
"rank": 3,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18d37",
"display_name": "5gHkycaaaaM2rRF",
"country": "VI",
"points": 9998,
"rank": 4,
"last_score_timestamp": ""
},
]
Returns the leaderboard of in a range, can be used for paging. e.g. start: 0, end: 3
[
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "0ioYXaaaaaa3V",
"country": "VI",
"points": 10000,
"rank": 1,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff1a3bb",
"display_name": "DOMsOa",
"country": "VI",
"points": 9999,
"rank": 2,
"last_score_timestamp": ""
},
{
"_id": "5fd6987b6cd5f9a6aff18418",
"display_name": "UOMNO",
"country": "VI",
"points": 9999,
"rank": 3,
"last_score_timestamp": ""
}
]
Works only on local environment using the mgodatagen tool, please install it to use this endpoint. This endpoint is designed to fill the database with mock data and feed the data into Redis. You have four options for number, 10, 100, 500 and 1000. These numbers all follow three zero's. Which means that 10 is 10000 users, 100 is 100000 users etc. Keep in mind that this operation takes time as Redis is single-threaded. You should wait till you get the response below.
{
"msg": "Database created and redis is initialized with leaderboard"
},
Creates a user
{
"display_name": "Super Mario",
"country": "IT",
"password": "1234" // to be converted and stored as a hash
}
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "Super Mario",
"password" "1234",
"rank": 100,
"last_score_timestamp": ""
},
User login, returns a token to be used in the Header Authorization key.
{
"display_name": "Super Mario",
"password": "1234"
}
{
token: <token>
}
Add the score to the users point field and updates her rank
{
"user_id": "5fd6987b6cd5f9a6aff1a6a8",
"score_worth": 1234,
"timestamp": timestamp
}
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "Super Mario",
"points": 2345,
"rank": 50,
"last_score_timestamp": timestamp
}
Returns the user with id profileID. Requires a token to be in the Authorization header
{
"_id": "5fd6987b6cd5f9a6aff1a6a8",
"display_name": "Super Mario",
"points": 2345,
"rank": 50,
"last_score_timestamp": timestamp
}