สร้างแอป Leaderboard บน Mobile ด้วย React Native Backend: Serverless Framework + Python on AWS Lambda Database: Upstash Redis
1. ทำไมต้อง Upstash Redis?
Leaderboard ต้องการ:
- เก็บ User + Score
- เรียงลำดับตาม Score
- Query Top N, Rank, Range ได้เร็ว
Redis Sorted Set (ZSET) ตอบโจทย์ทุกข้อ: O(log N) สำหรับ Insert/Update/Range Query
ข้อดีของ Upstash:
- Pay-as-you-go + Free Tier
- Setup ง่าย ไม่ต้อง Config Server
- HTTP REST API - ใช้ใน Lambda ได้เลย
1.1 เริ่มต้น Upstash
- สร้าง Account/DB ที่ Console
- ทดลอง CLI ใน Console:
# เพิ่ม User + Score
ZADD Leaderboard 15 "Jack_Thang"
# ดู Leaderboard ทั้งหมด (Descending)
ZRANGE Leaderboard 0 -1 WITHSCORES
2. Serverless Framework + AWS Lambda
Install & Config
# Install Serverless
npm install -g serverless
serverless create --template aws-python3 --path leaderboard-api
cd leaderboard-api
# Plugin สำหรับ Python Dependencies
serverless plugin install -n serverless-python-requirements
serverless.yml
service: leaderboard-api
frameworkVersion: '3'
provider:
name: aws
runtime: python3.9
region: us-east-1
plugins:
- serverless-python-requirements
functions:
addScore:
handler: handler.addScore
events:
- httpApi: 'POST /add'
getLeaderboard:
handler: handler.getLeaderboard
events:
- httpApi: 'GET /getLeaderboard'
requirements.txt
redis==4.0.2
handler.py
import json
import redis
import os
r = redis.Redis(
host=os.environ['REDIS_ENDPOINT'],
port=os.environ['REDIS_PORT'],
password=os.environ['REDIS_PASSWORD'],
charset="utf-8",
decode_responses=True
)
LEADERBOARD_KEY = "Leaderboard"
def addScore(event, context):
info = json.loads(event["body"])
score = info["score"]
player_name = f"{info['firstname']}_{info['lastname']}"
r.zadd(LEADERBOARD_KEY, {player_name: score})
return {
"statusCode": 200,
"body": json.dumps({"message": "Score added successfully!"})
}
def getLeaderboard(event, context):
# Descending order (Highest first)
score_list = r.zrange(LEADERBOARD_KEY, 0, -1, withscores=True, desc=True)
return {
"statusCode": 200,
"body": json.dumps({
"message": "Leaderboard returned successfully!",
"leaderboard": score_list
})
}
Deploy
serverless deploy -v
บันทึก Endpoints ที่ได้:
POST - https://xxx.execute-api.us-east-1.amazonaws.com/add
GET - https://xxx.execute-api.us-east-1.amazonaws.com/getLeaderboard
3. React Native Mobile App
Screen 1: Submit Score
async addScore() {
if (isNaN(this.state.score)) {
Alert.alert("Error", "Please enter a valid score.");
return;
}
if (!this.state.firstname || !this.state.lastname || !this.state.score) {
Alert.alert("Error", "Please fill in the blanks.");
return;
}
try {
const response = await fetch('https://xxx.execute-api.us-east-1.amazonaws.com/add', {
method: 'POST',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({
firstname: this.state.firstname,
lastname: this.state.lastname,
score: parseInt(this.state.score)
})
});
const data = await response.json();
if (data.message === "Score added successfully!") {
Alert.alert("Done!", "Score added successfully!");
} else {
Alert.alert("Error", "Please try again later.");
}
} catch (err) {
console.error(err);
Alert.alert("Error", "Please try again later.");
}
}
Screen 2: Leaderboard View
async componentDidMount() {
try {
const response = await fetch('https://xxx.execute-api.us-east-1.amazonaws.com/getLeaderboard', {
method: 'GET',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
});
const data = await response.json();
this.setState({ userlist: data.leaderboard, reRender: !this.state.reRender });
} catch (err) {
console.error(err);
}
}
// Render
render() {
return (
<View>
{this.state.userlist.map(([name, score], index) => (
<Text key={index}>#{index + 1} {name}: {score}</Text>
))}
</View>
);
}
สรุป
- Leaderboard ด้วย Redis Sorted Set = Fast, Scalable, Simple
- Serverless Framework จัดการ AWS Lambda ได้ง่าย
- Upstash Redis: No Server Management, HTTP API, Pay-per-use
- React Native เรียก REST API โดยตรง
Source Code: GitHub Repository