Simple Serverless Leaderboard API. It uses
AWS API GatewayandAWS Lambdato serve this Web API with Serverless model.Redisto control the concurrent updates on the same ranking document.S3to store it as permanently.
Since you are using Redis, strictly speaking, this is not Serverless. This dependency will be removed in the future by implementing the web-based state management for ActorSystem's concurrency control.
Request GET to /{serviceId}/{period}?limit=<number> with a header X-User for userId, then it returns by this form.
interface IRankRecord {
rank: number;
user: string;
score: string;
}
interface IRankView {
me: IRankRecord;
top: IRankRecord[];
around: IRankRecord[];
}aroundmeans somerecords near my rank which contains my rank, too.scoreshould bestringbecause its value is bigger thanNumber.MAX_SAFE_INTEGER.meandaroundfield can beundefinedwhen there is no my record.
$ curl "https://API-DOMAIN/STAGE/service_id/period" -H "X-User: test"
{
"top": [
{
"rank": 321,
"user": "test",
"score": "123456789123456789"
},
...
],
"around": [
...,
{
"rank": 321,
"user": "test",
"score": "123456789123456789"
},
...
],
"my": {
"rank": 321,
"user": "test",
"score": "123456789123456789"
},
}Request GET to /{serviceId}/{period}/me with a header X-User for userId.
$ curl "https://API-DOMAIN/STAGE/service_id/period/me" -H "X-User: test"
{
"rank": 321,
"user": "test",
"score": "123456789123456789"
}Request GET to /{serviceId}/{period}/top?offset=<number>&limit=<number>.
$ curl "https://API-DOMAIN/STAGE/service_id/period/top?offset=0&limit=10"
[{
"rank": 1,
"user": "test",
"score": "123456789123456789"
}, ...]Request GET to /{serviceId}/{period}/around?limit=<number> with a header X-User for userId.
$ curl "https://API-DOMAIN/STAGE/service_id/period/around?limit=10" -H "X-User: test"
[..., {
"rank": 321,
"user": "test",
"score": "123456789123456789"
}, ...]If you want to see more rankings from the current result, please request GET to /{serviceId}/{period}/{direction}?cursor=<user>&limit=<number> where direction is one of up and down.
For example, if we want to see more 10 higher rankings from the test user, it should request to /{serviceId}/{period}/up?cursor=test&limit=10. This is because this user field should be unique.
$ curl "https://API-DOMAIN/STAGE/service_id/period/up?limit=10"
[..., {
"rank": 320,
"user": "test2",
"score": "123456789123456799"
}]Request PUT to /{serviceId}/{period} with a header X-User for userId, then it returns by the form that is same with GET my API. The type of payload for score is string because it can be bigger than Number.MAX_SAFE_INTEGER.
- This API doesn't update a record when an old score is higher than a new score.
- This API can be slow if there is so many concurrent
PUTcalls. It is because it is on the actor model to manage the consistency of ranks whiel updating concurrently. Please see details in benchmark.
$ curl -XPUT "https://API-DOMAIN/STAGE/service_id/period" -H "X-User: test" -d "123456789123456789"
{
"rank": 321,
"user": "test",
"score": "123456789123456789"
}For admin purpose, it supports CLEAR command via DELETE request.
curl -XDELETE "https://API-DOMAIN/STAGE/service_id/period" -H "X-Auth: admin-secret"But if process.env.AUTH_KEY isn't set while deploying, X-Auth can be omitted and it can lead very horrible problem, that is resetting all of ranks by anonymous.
- Install dependencies via
yarncommand from your shell. - After editing some codes, deploy this stack via
yarn deploycommand.
yarn
yarn deployIt contains serverless-offline plugin, you test it locally using yarn start command.
yarn
yarn startAlso, you can debug this stack using yarn debug command.
MIT