Backend Infrastructure (Upstash)
Relevant source files
The following files were used as context for generating this wiki page:The backend for Farm Siege is a serverless architecture responsible for managing the global highscore leaderboard. It is built using Vercel Edge Functions which interact directly with an Upstash Redis instance via its REST API. This setup provides a lightweight, scalable, and persistent storage solution for player scores. Security against automated submissions is handled by Google reCAPTCHA v3.
The entire backend consists of two primary API endpoints: one for retrieving the list of highscores and another for submitting a new score. The frontend client communicates with these endpoints at specific points in the game flow, such as on the game over screen or when the highscore list is displayed.
Sources: [README.md:8](https://github.com/wotschofsky/farmsiege/blob/HEAD/README.md#L8), [api/highscores.ts](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts), [api/submitScore.ts](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts)
Architecture Overview
The system employs a simple three-tier serverless architecture. The game client (browser) makes HTTPS requests to Vercel Edge Functions. These functions contain the business logic for handling scores and communicate with the Upstash Redis database using its provided REST API for data persistence.
Sources: [api/highscores.ts:5-8](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts#L5-L8), [api/submitScore.ts:5-8](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L5-L8)
The following diagram illustrates the high-level architecture:
Sources: [api/highscores.ts:12-16](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts#L12-L16), [api/submitScore.ts:13-25, 68-72](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L13-L25)
Configuration
The backend functions rely on several environment variables for their operation. These must be configured in the Vercel project settings.
| Variable | Description | Used In |
|---|---|---|
UPSTASH_REDIS_REST_URL | The REST API endpoint URL for the Upstash Redis instance. | api/highscores.ts, api/submitScore.ts |
UPSTASH_REDIS_REST_TOKEN | The authorization token for the Upstash Redis REST API. | api/highscores.ts, api/submitScore.ts |
RECAPTCHA_SECRET | The secret key for the Google reCAPTCHA service, used for server-side validation. | api/submitScore.ts |
VITE_RECAPTCHA_SITE_KEY | The site key for Google reCAPTCHA, used by the frontend to generate tokens. | src/screens/GameOverScreen.ts, src/Game.ts |
Sources: [api/highscores.ts:5-8](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts#L5-L8), [api/submitScore.ts:5-8, 14-15](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L5-L8), [src/screens/GameOverScreen.ts:68-70](https://github.com/wotschofsky/farmsiege/blob/HEAD/src/screens/GameOverScreen.ts#L68-L70)
Data Storage (Upstash Redis)
Highscores are stored in a single Redis Sorted Set with the key highscores.
- Key:
highscores - Score: The player's integer score.
- Member: The player's name (a string).
This data structure is ideal for a leaderboard as it automatically keeps members ordered by their score, making it efficient to retrieve the top N scores.
Sources: [api/highscores.ts:12](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts#L12), [api/submitScore.ts:68](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L68)
API Endpoints
GET /api/highscores
This endpoint retrieves the top 10 highscores from the Upstash Redis store.
Flow
- The function receives a GET request.
- It sends a
ZREVRANGE highscores 0 9 WITHSCOREScommand to the Upstash Redis REST API. This command fetches the top 10 entries (indices 0 to 9) in descending order, along with their scores. - The function parses the flat array response from Redis (e.g.,
["Player1", "1000", "Player2", "950"]) into a structured JSON array of objects. - It returns this JSON array to the client.
Sources: [api/highscores.ts:11-34](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts#L11-L34), [src/store/MiscStore.ts:63-67](https://github.com/wotschofsky/farmsiege/blob/HEAD/src/store/MiscStore.ts#L63-L67)
The following sequence diagram shows the process of fetching highscores:
Sources: [api/highscores.ts:12-34](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts#L12-L34), [src/store/MiscStore.ts:63-82](https://github.com/wotschofsky/farmsiege/blob/HEAD/src/store/MiscStore.ts#L63-L82)
Response Payload
The endpoint returns a JSON array of score objects.
| Field | Type | Description |
|---|---|---|
name | string | The player's name. |
score | number | The player's score. |
Sources: [api/highscores.ts:27-30](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/highscores.ts#L27-L30), [src/components/Highscores.ts:12-15](https://github.com/wotschofsky/farmsiege/blob/HEAD/src/components/Highscores.ts#L12-L15)
POST /api/submitScore
This endpoint handles the submission of a new score from a player after a game ends. It includes validation and anti-spam measures.
Flow
- Request & Validation: The function receives a POST request with a JSON body. It uses
zodto validate that the body contains aname(string, 1-20 chars),score(integer), andrecaptcha(string) token. - reCAPTCHA Verification: The
recaptchatoken is sent to the Google reCAPTCHA API for verification. The submission is only considered valid if the verification is successful and the action's score is0.5or higher. - Database Write: If validation and reCAPTCHA checks pass, the function sends a
ZADD highscores GTcommand to the Upstash Redis REST API. TheGToption ensures that the score is only added if it is greater than the lowest score currently in the set (when the set is at capacity). The URL is constructed as/zadd/highscores/gt/${score}/${name}. - Response: The function returns a JSON object indicating success or failure.
Sources: [api/submitScore.ts:33-89](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L33-L89), [src/screens/GameOverScreen.ts:58-94](https://github.com/wotschofsky/farmsiege/blob/HEAD/src/screens/GameOverScreen.ts#L58-L94)
The sequence diagram below details the score submission process:
Sources: [api/submitScore.ts:13-89](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L13-L89), [src/screens/GameOverScreen.ts:68-85](https://github.com/wotschofsky/farmsiege/blob/HEAD/src/screens/GameOverScreen.ts#L68-L85)
Request Body Schema
| Field | Type | Constraints | Description |
|---|---|---|---|
name | string | min(1), max(20) | The player's name. |
score | number | int(), min(0) | The score achieved. |
recaptcha | string | - | The token from Google reCAPTCHA. |
Sources: [api/submitScore.ts:27-32](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L27-L32)
Security (Google reCAPTCHA)
To prevent spam and automated score submissions, the submitScore endpoint is protected by Google reCAPTCHA v3.
- Frontend: When a game ends, the main
Gamecomponent ensures the reCAPTCHA script is loaded. On theGameOverScreen, before submitting a score, the client requests a token from the reCAPTCHA service for thehighscoreaction. - Backend: The
submitScoreAPI handler receives this token and forwards it to Google'ssiteverifyendpoint along with theRECAPTCHA_SECRET. The request is only processed if Google confirms the token is valid and returns a confidence score of0.5or greater.
Sources: [Game.ts:80-88](https://github.com/wotschofsky/farmsiege/blob/HEAD/Game.ts#L80-L88), [src/screens/GameOverScreen.ts:68-70](https://github.com/wotschofsky/farmsiege/blob/HEAD/src/screens/GameOverScreen.ts#L68-L70), [api/submitScore.ts:13-25, 52-64](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L13-L25)
This diagram shows the reCAPTCHA validation logic within the submitScore function:
Sources: [api/submitScore.ts:35-89](https://github.com/wotschofsky/farmsiege/blob/HEAD/api/submitScore.ts#L35-L89)