Highscore API
Relevant source files
The following files were used as context for generating this wiki page:The Highscore API provides the backend functionality for the Farm Siege game's leaderboard. It consists of two serverless Vercel Edge Functions that handle fetching and submitting player scores. The data is persisted in an Upstash Redis instance, which acts as the database for the highscores.
This system is designed to be lightweight and scalable, leveraging edge computing for low-latency responses. It includes security measures like Google reCAPTCHA to prevent fraudulent score submissions. The client-side game application interacts with these API endpoints to display the leaderboard on the start and game-over screens, and to submit a new score when a game ends.
Architecture
The Highscore API is built on a serverless architecture using Vercel Edge Functions and Upstash Redis. The client-side application makes HTTP requests to the API endpoints, which are handled by the edge functions. These functions then communicate with the Upstash Redis database via its REST API to read or write score data.
Sources: api/highscores.ts, api/submitScore.ts
High-Level Architecture Diagram
This diagram illustrates the flow of data from the game client to the Upstash Redis database through the Vercel Edge Functions.
Environment Configuration
The API functions require specific environment variables to be set for connecting to the database and reCAPTCHA services. These are checked at runtime, and an error is thrown if they are missing.
| Variable | Description |
|---|---|
UPSTASH_REDIS_REST_URL | The REST API endpoint URL for the Upstash Redis instance. |
UPSTASH_REDIS_REST_TOKEN | The authorization token for the Upstash Redis instance. |
RECAPTCHA_SECRET | The secret key for the Google reCAPTCHA service. |
Sources: api/highscores.ts:5-7, api/submitScore.ts:7-9, api/submitScore.ts:13
API Endpoints
The API exposes two endpoints for managing highscores. Both are configured to run on Vercel's edge runtime.
Sources: api/highscores.ts:2-4, api/submitScore.ts:3-5
| Method | Endpoint | Description |
|---|---|---|
GET | /api/highscores | Fetches the top 10 highscores. |
POST | /api/submitScore | Submits a new score to the leaderboard. |
GET /api/highscores
This endpoint retrieves the top 10 scores from the Redis database. It uses the ZREVRANGE command on a sorted set named highscores to get the names and scores, ordered from highest to lowest.
The raw response from Redis is an array of alternating names and scores, which is then parsed into an array of objects.
Sources: api/highscores.ts:11-28
Data Flow
Response Payload
The endpoint returns a JSON array of score objects.
[
{ "name": "string", "score": "number" },
...
]
Sources: api/highscores.ts:23-26, src/components/Highscores.ts:11-14
POST /api/submitScore
This endpoint allows a player to submit their score. It expects a JSON body containing the player's name, score, and a reCAPTCHA token.
The handler first validates the request body against a zod schema. It then verifies the reCAPTCHA token with Google's API to ensure the request is from a legitimate user. If both checks pass, it uses the ZADD Redis command with the GT option to add the score only if it's greater than any existing score for that player.
Sources: api/submitScore.ts:30-74
Request Body Validation
The request body is validated using the zod library.
| Field | Type | Constraints |
|---|---|---|
name | string | min 1 character, max 20 characters |
score | number | integer, min 0 |
recaptcha | string | - |
Sources: api/submitScore.ts:25-29
Security: reCAPTCHA Verification
To prevent automated submissions, the endpoint validates a reCAPTCHA token. The token is sent to Google's siteverify API. The submission is considered valid only if the verification is successful (json.success is true) and the bot score is 0.5 or higher.
Sources: api/submitScore.ts:11-23, api/submitScore.ts:46-54
Data Flow
Client-Side Integration
The frontend game integrates with the Highscore API primarily through the MiscStore and the GameOverScreen component.
Fetching Highscores
The MiscStore contains the fetchHighscores method, which sends a GET request to /api/highscores. Upon receiving the scores, it sorts them and updates the application's state, causing UI components like Highscores to re-render with the new data.
Sources: src/store/MiscStore.ts:52-71
Submitting a Score
On the game-over screen, the submitScore method is called when the player enters their name. This method first retrieves a reCAPTCHA token using grecaptcha.execute. It then sends a POST request to /api/submitScore with the score, name, and token. The client optimistically triggers a re-fetch of the highscores if the submitted score was high enough to potentially make the leaderboard.
Sources: src/screens/GameOverScreen.ts:61-100
The reCAPTCHA script is loaded globally in the main Game component to be ready when needed.
Sources: src/Game.ts:68-77