Serverless Thực Chiến Trên AWS: URL Shortener + Analytics Realtime
Dựng một sản phẩm serverless hoàn chỉnh trên AWS từ số không: một dịch vụ rút gọn URL có analytics realtime. Series không dạy từng dịch vụ rời rạc mà build một backend production-ready xuyên suốt — Lambda, API Gateway, DynamoDB single-table, Cognito, EventBridge, Step Functions, WebSocket API — rồi vận hành nó như thật: idempotency, DLQ, X-Ray tracing, cold start, IAM least-privilege, CI/CD canary, phân tích chi phí và load test. Mọi hạ tầng dựng bằng AWS SAM, code Node.js + TypeScript, mọi lệnh chạy thật trên AWS, code lưu tại github.com/nghiadaulau/serverless-url-shortener-aws. Bám tài liệu chính thức AWS.
Serverless Là Gì và Khi Nào Nên Dùng
Bài mở đầu series: serverless thực ra nghĩa là gì (không phải 'không có server'), nó đánh đổi cái gì để lấy cái gì, khi nào KHÔNG nên dùng, và sản phẩm ta sẽ dựng xuyên suốt — một dịch vụ rút gọn URL có analytics realtime. Kèm sơ đồ kiến trúc tổng thể và lộ trình từng phần.
Dựng Môi Trường: AWS SAM và Hàm Lambda Đầu Tiên
Cài AWS SAM CLI, dựng khung project cho URL shortener (TypeScript + esbuild), rồi deploy một hàm Lambda thật ra AWS qua Function URL, gọi nó bằng curl, chạy thử local trong Docker, và xóa sạch bằng một lệnh. Kèm bài học về việc SAM CLI phải đủ mới để hỗ trợ runtime nodejs22.x.
Lambda Chạy Code Của Bạn Ra Sao Bên Trong
Mổ vòng đời môi trường thực thi của Lambda: ba pha Init, Invoke, Shutdown, vì sao có cold start, và code static chạy một lần ra sao. Đo cold start thật (Init Duration), rồi đo quan hệ memory–CPU bằng cùng một việc CPU ở 128 MB và 1769 MB. Hiểu phần này để các quyết định về hiệu năng và chi phí sau này có cơ sở.
API Gateway: HTTP API hay REST API, và Dựng Route Đầu Tiên
API Gateway có hai loại API với hai bộ tính năng và hai mức giá. So sánh HTTP API và REST API để chọn đúng cho URL shortener, rồi dựng hai route thật: POST /links tạo link và GET /{code} chuyển hướng 301. Xử lý validate, path parameter, và CORS preflight để bài dashboard sau này không vỡ.
DynamoDB Single-Table Design: Bắt Đầu Từ Câu Hỏi, Không Phải Từ Bảng
Thiết kế DynamoDB đi ngược với cơ sở dữ liệu quan hệ: bắt đầu từ các câu hỏi truy vấn, không từ bảng. Bài này liệt kê access pattern của URL shortener, giải thích partition key và sort key, rồi dựng một single-table với item collection — link và thống kê click của nó nằm chung một phân vùng, lấy về trong một query. Tạo bảng thật, put và query item thật để thấy cơ chế.
Global Secondary Index và Sparse Index: Mở Đường Truy Vấn Mới
Bảng single-table ở bài trước trả lời nhanh câu 'mở link theo code', nhưng bất lực với 'liệt kê link của một người dùng'. Bài này thêm một global secondary index để đảo khóa và mở đúng đường truy vấn đó, rồi dùng sparse index để index chỉ chứa link mà loại bỏ các bản ghi thống kê một cách tự nhiên. Tạo GSI thật, query thật để thấy cơ chế.
Nối DynamoDB Vào Code: Ghi An Toàn và Atomic Counter
Gắn hai handler vào DynamoDB thật: tạo link ghi item với conditional write để không đè trùng mã, mở link tra cứu rồi đếm click bằng atomic counter. Bắn nhiều lượt mở song song để thấy atomic counter đếm chính xác, và bắt gặp một giới hạn thật của tài khoản — concurrency Lambda — khi 40 trong 50 request trả về 503.
Cognito và JWT Authorizer: Chỉ Người Đăng Nhập Mới Tạo Được Link
Thêm người dùng thật bằng Amazon Cognito. Dựng user pool phát JWT, gắn JWT authorizer của HTTP API để bảo vệ route tạo link trong khi route mở link vẫn công khai, và cho handler đọc danh tính người dùng từ claim trong token thay vì gán cứng. Tạo user thật, lấy token thật, gọi API có và không có token để thấy ranh giới.
Multi-Tenant: Mỗi Người Một Vùng Dữ Liệu, và Chặn IDOR
Biến URL shortener thành multi-tenant đúng nghĩa. Thêm route liệt kê link giới hạn theo danh tính trong token, và route xóa link kiểm quyền sở hữu ngay trong thao tác ghi để một người không xóa được link của người khác kể cả khi đoán đúng mã. Test với hai người dùng thật để thấy ranh giới giữ vững.
EventBridge: Tách Việc Ghi Nhận Click Khỏi Đường Chuyển Hướng
Mở đầu phần event-driven. Thay vì đếm click ngay trong handler chuyển hướng, mỗi lượt mở link sẽ phát một sự kiện lên một custom event bus của EventBridge, và một consumer riêng xử lý nó. Dựng bus, cho resolve phát sự kiện, gắn một consumer qua event pattern, rồi mở link thật để thấy sự kiện chảy qua bus tới consumer.
Đếm Click An Toàn: Idempotency, DLQ và Partial Batch Failure
Biến consumer ghi log ở bài trước thành một aggregator thật. Chèn SQS giữa EventBridge và Lambda để có batch, retry và dead-letter queue, đếm click vào DynamoDB bằng transaction để vừa cộng bộ đếm vừa chống đếm trùng trong một thao tác nguyên tử, và báo lỗi theo từng message để chỉ thử lại cái hỏng. Test thật: gửi trùng sự kiện đếm một lần, sự kiện lỗi rơi vào DLQ.
WebSocket API: Đẩy Số Click Xuống Dashboard Realtime
Làm phần realtime của dashboard. Dựng WebSocket API của API Gateway với route $connect và $disconnect, lưu mỗi kết nối vào DynamoDB gắn với link nó đang theo dõi, rồi cho aggregator đẩy số click mới xuống đúng các kết nối đang mở mỗi khi đếm xong. Mở một kết nối thật, click link, và thấy số nhảy về trình duyệt không cần tải lại.