Remix** เป็น Full Stack Framework ที่ให้ API จัดการ Session ได้ในตัว บทความนี้จะสอนใช้ **Upstash Redis** เป็น Session Store แทน File System
Session คืออะไร?
กลไกให้ Server และ Client แชร์ State/User Data เช่น Authentication State, Shopping Cart, Flash Messages
Remix Docs: Session Documentation
ทำไมต้อง Upstash Redis?
- Key-Value Nature: Session = Key (Session ID) : Value (Serialized Data) - เข้ากันได้กับ Redis สุดๆ
- Built-in Expiration: Redis มี TTL อัตโนมัติ - ไม่ต้อง Cleanup Expired Sessions เอง
- Encryption at Rest: Upstash เข้ารหัสข้อมูลใน Disk - ปลอดภัยสำหรับ Sensitive Data
- HTTP REST API: ใช้จาก Serverless/Edge Functions ได้ง่าย (ไม่ต้อง TCP Connection)
Implementation Steps
Step 1: Setup Upstash Credentials
# 1. สร้าง DB ที่ https://upstash.com/
# 2. Copy UPSTASH_REDIS_REST_URL และ UPSTASH_REDIS_REST_TOKEN ใส่ .env
# 3. Install dotenv
npm install --save-dev dotenv
# 4. Update package.json dev script
"dev": "dotenv/config node_modules/.bin/remix dev"
Step 2: Implement createSessionStorage (sessions/upstash.server.ts)
// sessions/upstash.server.ts
import * as crypto from "crypto";
import { createSessionStorage } from "remix";
const upstashRedisRestUrl = process.env.UPSTASH_REDIS_REST_URL;
const headers = {
Authorization: `Bearer ${process.env.UPSTASH_REDIS_REST_TOKEN}`,
Accept: "application/json",
"Content-Type": "application/json",
};
const expiresToSeconds = (expires: Date) => {
const now = new Date();
const expiresDate = new Date(expires);
const secondsDelta = (expiresDate.getTime() - now.getTime()) / 1000;
return secondsDelta < 0 ? 0 : Math.floor(secondsDelta);
};
export function createUpstashSessionStorage({ cookie }: any) {
return createSessionStorage({
cookie,
async createData(data, expires) {
const randomBytes = crypto.randomBytes(8);
const id = Buffer.from(randomBytes).toString("hex");
await fetch(`${upstashRedisRestUrl}/set/${id}?EX=${expiresToSeconds(expires)}`, {
method: "post",
body: JSON.stringify({ data }),
headers,
});
return id;
},
async readData(id) {
const response = await fetch(`${upstashRedisRestUrl}/get/${id}`, { headers });
try {
const { result } = await response.json();
return JSON.parse(result).data;
} catch {
return null;
}
},
async updateData(id, data, expires) {
await fetch(`${upstashRedisRestUrl}/set/${id}?EX=${expiresToSeconds(expires)}`, {
method: "post",
body: JSON.stringify({ data }),
headers,
});
},
async deleteData(id) {
await fetch(`${upstashRedisRestUrl}/del/${id}`, { method: "post", headers });
},
});
}
Key Points:
- Cookie มี `expires` เป็น JS Date → แปลงเป็นวินาทีให้ Redis (`expiresToSeconds`)
- ใช้ `crypto.randomBytes` สร้าง Session ID (เหมือน Core Remix)
- Redis จะ Auto-delete Expired Keys
Step 3: ใช้งานใน App (sessions.server.ts)
// sessions.server.ts
import { createCookie } from "remix";
import { createUpstashSessionStorage } from "~/sessions/upstash.server";
const EXPIRATION_DURATION_IN_SECONDS = 10; // Demo: 10 วินาที
const expires = new Date();
expires.setSeconds(expires.getSeconds() + EXPIRATION_DURATION_IN_SECONDS);
const sessionCookie = createCookie("__session", {
secrets: ["r3m1xr0ck1"],
sameSite: true,
expires,
});
const { getSession, commitSession, destroySession } = createUpstashSessionStorage({
cookie: sessionCookie,
});
export { getSession, commitSession, destroySession };
Step 4: ใช้ Session ใน Route (routes/index.tsx)
// routes/index.tsx
import type { LoaderFunction } from "remix";
import { json, useLoaderData } from "remix";
import { commitSession, getSession } from "~/sessions.server";
export const loader: LoaderFunction = async ({ request }) => {
const session = await getSession(request.headers.get("Cookie"));
const myStoredData = session.get("myStoredData");
if (!myStoredData) {
// ไม่มี Session → สร้างใหม่
session.set("myStoredData", "Some data");
return json(
{ message: "Created new session" },
{ headers: { "Set-Cookie": await commitSession(session) } }
);
}
// มี Session → แสดงข้อมูล
return json({ message: `Showing Session info: ${myStoredData}` });
};
export default function() {
const data = useLoaderData();
return <div>{data.message}</div>;
}
Step 5: Deploy
Deploy ไป Vercel/Netlify/Cloudflare Pages ได้เลย แค่ตั้ง Environment Variables ใน Platform
Appendix: Local Development vs Production
พัฒนา Local ใช้ File System, Production ใช้ Upstash:
// sessions.server.ts
import { createFileSessionStorage } from "remix";
const { getSession, commitSession, destroySession } = process.env.NODE_ENV === "development"
? createFileSessionStorage({ cookie: sessionCookie, dir: "./sessions" })
: createUpstashSessionStorage({ cookie: sessionCookie });
สรุป
- Remix `createSessionStorage` ทำให้ Custom Session Store เขียนง่าย
- Upstash Redis เหมาะสำหรับ Session: Key-Value, TTL, Encryption, HTTP API
- Deploy ไป Serverless/Edge ไหนก็ได้
Example Repo: Remix Redis Upstash Session Example