ในบทความนี้ ผมจะแนะนำการสร้างบริการย่อลิงก์ (URL Shortener) แบบ serverless ที่สร้างบน Upstash Redis, AWS Lambda (Python) และ AWS API Gateway จุดประสงค์หลักคือสาธิตกระบวนการสร้าง Lambda function ด้วย Python เชื่อมต่อกับ Redis และใช้งานผ่าน API
คุณสามารถดูตัวอย่างการนำไปใช้ได้ที่: URL Shortener

หมายเหตุ: URL ผลลัพธ์เป็นเพื่อการสาธิตเท่านั้น URL shortener จริงจะมีโดเมนสั้นและง่ายกว่านี้ เราจึงเน้นที่การทำงานของฟังก์ชันหลัก
รายละเอียดโปรเจกต์
โปรเจกต์ประกอบด้วย ฐานข้อมูล Upstash Redis หนึ่งฐานข้อมูล สอง Lambda functions เขียนด้วย Python และ API Gateway บน AWS ผู้ใช้สามารถใช้งานผ่านเว็บอินเตอร์เฟซหรือ API ได้
Lambda function shortener สร้างคีย์ URL สั้นจาก URL ยาวที่ส่งมาผ่าน query parameter ของ HTTP request จากนั้นเก็บคู่คีย์-ค่า (short URL key และ long URL) ไว้ใน Upstash Redis ก่อนส่ง short URL กลับให้ผู้ใช้
Lambda function redirector ทำหน้าที่ redirect ผู้ใช้จาก short URL ไปยัง long URL ที่ตรงกัน โดยดึง long URL จาก Redis ด้วย short URL key และส่ง HTTP 302 redirection response กลับไปยังเบราว์เซอร์ของผู้ใช้ ฟังก์ชันนี้ช่วยให้ผู้ใช้เข้าถึงเนื้อหาที่ต้องการโดยไม่ต้องจดจำหรือพิมพ์ URL ยาว
สุดท้าย เราจะสร้าง API Gateway เพื่อใช้งาน Lambda functions ทั้งสอง API นี้จะเชื่อมต่อกับ frontend URL shortener แบบพื้นฐาน

ตั้งค่า Upstash Redis
เราสามารถสร้าง Redis Database บน Upstash Console ได้ หลังล็อกอิน คลิกปุ่ม Create Database ใช้เวลาเพียงไม่กี่วินาที คุณก็พร้อมใช้งาน จากนั้นคัดลอกตัวแปร UPSTASH_REDIS_REST_HOST, UPSTASH_REDIS_REST_PORT และ UPSTASH_REDIS_REST_PASSWORD จากส่วน Details ไปเก็บไว้ในไฟล์ เราจะนำตัวแปรเหล่านี้ไปใช้ใน AWS environment variables
สร้าง Serverless Functions บน AWS Lambda
ต่อไป เราจะสร้าง Python functions แบบ serverless หลังล็อกอิน AWS เข้าไปที่ AWS Lambda จาก Dashboard สร้าง lambda function ใหม่

Lambda Function สำหรับย่อ URL
ขั้นแรก ให้เพิ่ม environment variables จากหน้าฟังก์ชัน shortener ไปที่ Configuration > Environment variables > Edit > Add environment variable เพิ่ม UPSTASH_REDIS_REST_HOST, UPSTASH_REDIS_REST_PORT และ UPSTASH_REDIS_REST_PASSWORD เป็น keys พร้อมค่าที่ได้จาก Upstash Console
จากนั้นกลับไปที่ส่วน Code ของฟังก์ชัน shortener วางโค้ดด้านล่างเพื่อเชื่อมต่อกับ Upstash Redis Database
import random
import string
import json
import redis
# Create a redis client
redis_client = redis.Redis(
host=UPSTASH_REDIS_REST_HOST,
port=UPSTASH_REDIS_REST_PORT,
password=UPSTASH_REDIS_REST_PASSWORD
)
ต่อมา เราจะเขียนอัลกอริทึมหลักสำหรับ shortener โค้ดด้านล่างทำสิ่งต่อไปนี้:
- รับ query parameter
long_urlจาก API Request URL - สร้างคีย์สุ่ม 7 ตัวอักษสำหรับ shortened URL
- ตั้งค่าค่าของคีย์นี้ใน Upstash Redis เป็น long URL
- ส่ง HTTP 200 Response พร้อม short URL เต็มใน body
เพิ่มฟังก์ชัน generate_short_url และ lambda_handler เข้าไปด้านล่างการสร้าง Redis client
def generate_short_url():
# Define the character set for generating the short URLs
CHARSET = string.ascii_letters + string.digits
# Generate a random short URL using the character set
short_url = ''.join(random.choice(CHARSET) for _ in range(7))
# Check if the short URL already exists in Redis
if redis_client.exists(short_url):
# If it does, recursively generate a new short URL
return generate_short_url()
# Otherwise, return the new short URL
return short_url
def lambda_handler(event, context):
long_url = event["queryStringParameters"]['long_url']
base_url = f"https://{event['headers']['Host']}/{event['requestContext']['stage']}/"
# Generate a unique short URL
short_url_key = generate_short_url()
# Store the mapping between the short URL and long URL in Redis
redis_client.set(short_url_key, long_url)
# Return the short URL to the client
response_body = {
'short_url': base_url + short_url_key,
}
response = {
'statusCode': 200,
'headers': {'ContentType': 'application/json', "Access-Control-Allow-Origin": "*"},
'body': json.dumps(response_body)
}
return response
คลิก Deploy แม้โค้ดจะพร้อมแล้ว แต่ถ้าทดสอบ lambda ตอนนี้ คุณจะได้ response ดังนี้จาก Lambda:
Unable to import module 'lambda_function': No module named 'redis'
เพราะเพื่อใช้ external Python libraries เราต้องสร้าง custom package แล้วแนบไปกับ Lambda มีหลายวิธี แต่วิธีแนะนำคือใช้ Lambda Layers อย่าปวดหัว มันง่ายกว่าที่คิด
สร้าง Layer
ก่อนอื่น ต้องสร้างและ zip package เปิด terminal รันคำสั่งด้านล่าง จะได้ไฟล์ requirements-package.zip ในโฟลเดอร์ packages พร้อม Redis ติดตั้งแล้ว
mkdir packages
cd packages
python3 -m venv venv
source venv/bin/activate
mkdir python
cd python
pip install redis -t .
rm -rf *dist-info
cd ..
zip -r requirements-package.zip python
กลับไปที่ฟังก์ชัน shortener บน AWS ไปที่ Layers จากแถบด้านข้าง คลิก Create layer กรอกข้อมูลและอัปโหลด requirements-package.zip คล้ายกับตัวอย่าง

เมื่อสร้าง layer เรียบร้อย ต้อง attach layer นี้เข้ากับฟังก์ชัน shortener ไปที่ส่วน Layers ของฟังก์ชัน คลิก Add a layer

ตอนนี้ควรย่อ URL ได้แล้ว เหลือแค่ดึง long URL จาก short_url key และ redirect เบราว์เซอร์ ไปสร้าง lambda function redirector
Lambda Function สำหรับ Redirect URL
ขั้นตอนการสร้าง redirector lambda เหมือนด้านข้างบน ทั้งหมด:
- สร้าง lambda function ชื่อ
redirectorจาก Lambda dashboard - ตั้งค่า environment variables
- วางโค้ดด้านล่าง
- สร้าง layer ด้วย
requirements-package.zip(ใช้redis-library-layerเดิมได้ เนื่องจาก requirements เหมือนกัน) - เพิ่ม layer เข้ากับ lambda function
อัลกอริทึมหลักสำหรับ redirector lambda:
import json
import redis
# Create a redis client
redis_client = redis.Redis(
host=UPSTASH_REDIS_REST_HOST,
port=UPSTASH_REDIS_REST_PORT,
password=UPSTASH_REDIS_REST_PASSWORD
)
def lambda_handler(event, context):
# Get the short URL from the request path
short_url = event['pathParameters']['short_url']
# Look up the long URL associated with the short URL in Redis
long_url = redis_client.get(short_url).decode('utf-8').strip('"')
long_url = format_url_for_redirection(long_url)
# If the short URL doesn't exist, return a 404 error
if not long_url:
response = {
'statusCode': 404,
'body': json.dumps({'error': 'Short URL not found'})
}
return response
# Otherwise, redirect the user to the long URL
response = {
'statusCode': 302,
'headers': {'Location': long_url},
'body': ''
}
return response
def format_url_for_redirection(url):
if not url.startswith("https://") and not url.startswith("https://"):
url = "https://" + url
return url
Lambda functions พร้อมแล้ว! แต่เพื่อให้ URL shortener ใช้งานได้จริง เราต้องมีวิธีเรียกใช้ lambdas มีหลายวิธี แต่ที่นี่เราจะใช้ AWS API Gateway
สร้าง API Gateway บน AWS
เข้า API Gateway จาก AWS Console คลิก Create API สร้าง REST API หลังตั้งค่าพื้นฐาน API จะพร้อมในไม่กี่วินาที

ต่อไป ต้องเชื่อม API Gateway กับ Lambda functions ในส่วน Resources ไปที่ Actions > Create Resource ใส่ resource name และ resource path

คลิกที่ path /shortener ใน Resources ไปที่ Actions > Create Method > Select GET > Confirm ส่วนนี้สำคัญมาก การตั้งค่านี้ทำให้ส่งข้อมูล request จาก API Gateway ไป Lambda functions ได้ ตรวจสอบให้ถูกต้อง!

เชื่อม shortener function กับ API แล้ว ย่อ URL ได้ แต่ยังต้องเชื่อม redirector Lambda function เข้า API ด้วย คลิกที่ / ใน Resources ไปที่ Actions > Create Resource

คลิกที่ /{short_url} ใน Resources แล้ว Actions > Create Method > Choose GET > Confirm

API พร้อมแล้ว ขั้นสุดท้ายคือ deploy API ไปที่ Actions > Deploy API > Create New Stage กรอกข้อมูลและ deploy ตอนนี้เรามี public API ในส่วน Stages จะเห็น public Invoke URL
เหลืองานสุดท้าย: trigger Lambda Functions ผ่าน serverless-shortener-API gateway
เข้าไปที่ฟังก์ชัน shortener ในส่วน Function overview คลิก Add Trigger

เพิ่ม trigger ให้ทั้งสองฟังก์ชัน Public API และ Lambda functions พร้อมใช้งานแล้ว สำหรับฟังก์ชัน shortener URL path จะเป็น:
<YOUR_INVOKE_URL>/shortener?short_url="<LONG_URL>"
สำหรับฟังก์ชัน redirector ต้องใช้ response จาก shortener โครงสร้าง URL trigger:
<YOUR_INVOKE_URL>/<SHORT_URL_KEY>
คลิกหรือส่ง HTTP Request จะ redirect ไปยัง long URL บริการพร้อมใช้งาน แต่เราจะสร้าง web interface เพิ่มเติม
สร้าง Web Interface และจัดการการใช้งานฟังก์ชัน
ใช้ HTML, Bootstrap และ JavaScript สำหรับ frontend เมื่อผู้ใช้พิมพ์ URL และกดปุ่ม Shorten จะได้ URL ที่ redirect ไปยังเว็บไซต์ต้นทาง

เว็บไซต์นี้เรียบง่าย มีไม่กี่องค์ประกอบ เริ่มจากเพิ่มบรรทัดนี้ในบล็อก <head> ของ HTML เพื่อใช้ Bootstrap features
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"/>
ภายในบล็อก <body> จะมี HTML elements หลัก
<div class="container mt-5">
<h1 class="mb-4 text-center">URL Shortener</h1>
<div class="row justify-content-center">
<div class="col-md-6">
<div class="input-group mb-3">
<input type="text" class="form-control" id="url-input" placeholder="Enter URL to shorten" />
<div class="input-group-append">
<button class="btn btn-primary" type="button" id="shorten-btn"> Shorten </button>
</div>
</div>
<a id="short-url" class="d-none"></a>
</div>
</div>
</div>
หลังเพิ่ม elements แล้ว เหลือแค่ดึงข้อมูลจาก API Gateway จัดการด้วย JavaScript ก่อนจบบล็อก <body> ใส่ฟังก์ชันดึง URL อย่าลืมแทนที่ <INVOKE_URL> ด้วยของคุณจาก AWS API Gateway
<!-- Add Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<!-- Add custom JS -->
<script>
const shortenBtn = document.getElementById("shorten-btn");
const urlInput = document.getElementById("url-input");
const urlOutput = document.getElementById("short-url");
shortenBtn.addEventListener("click", async function () {
let long_url = urlInput.value;
const apiUrl = `/shortener?long_url="${long_url}"`;
const response = await fetch(apiUrl);
const data = await response.json();
shortUrl = data.short_url;
urlOutput.innerHTML = shortUrl;
urlOutput.href = shortUrl;
urlOutput.classList.remove("d-none");
});
</script>
สรุปและแนวทางปรับปรุงโปรเจกต์
เราได้นำฟีเจอร์หลักของ serverless URL shortener มาใช้งานแล้ว หวังว่าจะเป็นตัวอย่างที่ชัดเจนสำหรับการสร้าง Python Lambda functions และการใช้งาน ผมมีข้อเสนอแนะสำหรับการปรับปรุงต่อไป:
- ตั้งค่า custom domain สำหรับ URL shortener
Invoke URLของ AWS ค่อนข้างซับซ้อน เนื่องจากจุดประสงค์หลักคือสาธิต AWS Lambda และ Upstash Redis จึงไม่ได้ขยายเรื่องนี้ แต่ถ้าต้องการสามารถเปลี่ยน URL ให้สั้นและง่ายขึ้นได้จาก AWS API Gateway - นำ Upstash Rate Limit มาใช้กับ API โปรเจกต์นี้ใช้ free tier แต่ถ้าจะสร้างบริการสาธารณะจริง ควรตั้งค่าจำกัดคำขอเพื่อป้องกันต้นทุนที่ไม่คาดคิด
- Host โปรเจกต์นี้บน Vercel
ขอบคุณที่อ่านจบครับ