Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

จัดการ Feature Flags ด้วย Upstash Redis ใน Remix Framework

Remix** เป็น Full Stack React Framework ที่ผูก Frontend-Backend ใกล้ชิดผ่าน Data Loaders และ Form Actions บทความนี้สาธิตการสร้าง Feature Flag System ด้วย Upstash Redis

Source Code: GitHub Repository

1. Setup Project

npx create-remix@latest
npm add --save-dev dotenv

ไฟล์ .env (เพิ่มใน .gitignore):

UPSTASH_REDIS_REST_URL="https://your-db.upstash.io"
UPSTASH_REDIS_REST_TOKEN="your-token"

Update package.json:

{
  "scripts": {
    "dev": "node -r dotenv/config node_modules/.bin/remix dev"
  }
}

2. Data Model: Redis Hash

Feature Flags เก็บใน Redis Hash `features`:

{
  "chart": "1",
  "graph": "0"
}

Commands ที่ใช้:

  • HGETALL - ดึงทั้งหมด
  • HSET - Enable/Disable Flag
  • HDEL - ลบ Flag
  • HMGET - ดึงหลาย Flag พร้อมกัน

3. Management Page (app/routes/features.tsx)

Loader: โหลด Flags ทั้งหมด

import { Redis } from "@upstash/redis";

type LoaderData = { features: Array<[string, boolean]> };

const loadAllFeatures = async (): Promise => {
  const redis = Redis.fromEnv();
  const data = await redis.hgetall("features");
  const features: Array<[string, boolean]> = [];

  for (let i = 0; i < data.length; i += 2) {
    features.push([data[i], data[i + 1] === "1"]);
  }

  return features.sort((a, b) => a[0].localeCompare(b[0]));
};

export const loader: LoaderFunction = async (): Promise => {
  const features = await loadAllFeatures();
  return { features };
};

Action: จัดการ Create/Enable/Disable/Delete

export const action: ActionFunction = async ({ request }) => {
  const formData = await request.formData();
  const feature = formData.get("feature") as string;
  const action = formData.get("_action") as string;

  if (!feature) return json({ error: "Please provide a feature" });

  const redis = Redis.fromEnv();

  switch (action) {
    case "create":
    case "enable":
      await redis.hset("features", { [feature]: 1 });
      break;
    case "disable":
      await redis.hset("features", { [feature]: 0 });
      break;
    case "delete":
      await redis.hdel("features", feature);
      break;
  }

  return redirect("/features");
};

UI Components

// Add Feature Form
const AddFeature = () => (
  <Form method="post" replace>
    <input type="hidden" name="_action" value="create" />
    <input type="text" name="feature" required placeholder="name" />
    <button type="submit">Add</button>
  </Form>
);

// Feature List with Enable/Disable/Delete
const FeatureList = () => {
  const { features } = useLoaderData<LoaderData>();

  return (
    <ul>
      {features.map(([feature, active]) => (
        <li key={feature}>
          <Form method="post" replace>
            <input type="hidden" name="_action" value={active ? "disable" : "enable"} />
            <input type="hidden" name="feature" value={feature} />
            <button type="submit" className="btn-naked">{active ? "💪" : "🦾"}</button>
          </Form>
          <span>{feature}</span>
          <Form method="post" replace>
            <input type="hidden" name="_action" value="delete" />
            <input type="hidden" name="feature" value={feature} />
            <button type="submit">Delete</button>
          </Form>
        </li>
      ))}
    </ul>
  );
};

// Main Page
export default function Features() {
  return (
    <div>
      <h1>Features</h1>
      <AddFeature />
      <FeatureList />
    </div>
  );
}

4. ใช้ Feature Flags ใน App

โหลด Flag เฉพาะที่ต้องการด้วย HMGET:

type LoaderData = { features: Record };

const loadFeatures = async (keys: string[]): Promise> => {
  const redis = Redis.fromEnv();
  const data = await redis.hmget("features", ...keys);

  return keys.reduce((acc, key, index) => {
    acc[key] = data[index] === "1";
    return acc;
  }, {});
};

export const loader: LoaderFunction = async (): Promise => {
  const features = await loadFeatures(["chart", "graph"]);
  return { features };
};

ใช้ใน Component:

export default function Index() {
  const { features } = useLoaderData<LoaderData>();

  return (
    <div>
      <h1>Dashboard</h1>
      {features.chart ? <h2>Chart</h2> : <h2>No Chart</h2>}
      {features.graph ? <h2>Graph</h2> : <h2>No Graph</h2>}
    </div>
  );
}

สรุป

  • Remix Loaders + Actions = Backend/Frontend คบคู่กันในไฟล์เดียว
  • Upstash Redis Hash เหมาะสำหรับ Feature Flags (Key-Value, Atomic Operations)
  • REST API ทำงานได้ใน Edge/Serverless ทุก Platform
  • ไม่ต้อง Setup GraphQL API แยก - ใช้ Web Standards (Forms) ตรงๆ