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

การใช้ Upstash Redis กับ Remix

Remix เป็นแนวคิดใหม่ในความหมายของเฟรมเวิร์ก React แบบฟูลสแต็ค โดยเน้นที่มาตรฐานเว็บที่มีอยู่และเชื่อมโยงส่วนหน้าอย่างใกล้ชิดกับแบ็กเอนด์ การมีเพศสัมพันธ์ที่แน่นแฟ้นนี้เป็นการสูดอากาศบริสุทธิ์เมื่อคุณเห็นว่าการโหลดข้อมูลลงในส่วนประกอบ React ของคุณนั้นง่ายเพียงใด หรือวิธีประมวลผลข้อมูลที่ส่งจากแบบฟอร์ม

ในบทความนี้ เราจะเห็นพลังของ Remix โดยการสร้างระบบการจัดการฟีเจอร์แฟล็กอย่างง่ายโดยใช้ Upstash Redis เป็นฐานข้อมูล

สามารถดูซอร์สโค้ดแบบเต็มได้ที่นี่

ตั้งค่า

คุณจะได้รับแอป Remix ใหม่ล่าสุดโดยเรียกใช้ npx create-remix@latest และเลือกสภาพแวดล้อมการปรับใช้ที่คุณต้องการ ฉันใช้ Vercel แต่ไม่ควรสร้างความแตกต่างให้กับบทช่วยสอนนี้

มีสองวิธีที่คุณสามารถเชื่อมต่อกับ Upstash Redis:วิธีแรกคือผ่านการเชื่อมต่อ TCP ซึ่งคุณสามารถใช้ไลบรารีไคลเอ็นต์ Redis มาตรฐานที่คุณคุ้นเคยได้ ประการที่สองคือผ่าน REST API ของ Upstash เราจะใช้ตัวเลือกที่สองเพราะสามารถใช้ได้ในทุกสภาพแวดล้อมแบบไร้เซิร์ฟเวอร์ เช่น สภาพแวดล้อม Cloudflare Workers ที่ Remix สามารถใช้งานได้ Upstash มีแพ็คเกจที่เลียนแบบคำสั่ง Redis จริง ทำให้ง่ายต่อการทราบว่าควรเรียกใช้ฟังก์ชันใด

ตอนนี้เราต้องการวิธีจัดเก็บตัวแปรสภาพแวดล้อมสองแบบที่จำเป็นในการเชื่อมต่อกับฐานข้อมูล Upstash Redis ของเรา Remix ไม่ได้มาพร้อมกับการรองรับ env var แบบสำเร็จรูป แต่สามารถทำได้โดยการเพิ่ม dotenv เป็นการพึ่งพาการพัฒนา

npm add --save-dev dotenv

ใน .env . ของเรา ไฟล์ (ซึ่งควรเพิ่มใน .gitignore ) เราสามารถตั้งค่า env vars สองรายการที่จำเป็นในการเชื่อมต่อกับ Upstash Redis @upstash/redis แพ็คเกจจะตรวจจับสิ่งเหล่านี้โดยอัตโนมัติ ดังนั้นจึงไม่จำเป็นต้องเชื่อมต่อภายในรหัสของเรา ค่าเหล่านี้สามารถพบได้ในแดชบอร์ด Upstash หลังจากสร้างฐานข้อมูล Redis ใหม่

UPSTASH_REDIS_REST_URL="https://..."
UPSTASH_REDIS_REST_TOKEN="..."

เราจำเป็นต้องอัปเดต dev . ของเรา สคริปต์เพื่อให้ dotenv เลือก env vars สคริปต์อื่นๆ ยังคงเหมือนเดิม

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

การจัดเก็บข้อมูลคุณลักษณะ

แฟล็กคุณลักษณะอาจซับซ้อนอย่างไม่น่าเชื่อ โดยมีแผนเปิดตัวเป็นเปอร์เซ็นต์ของฐานผู้ใช้ของคุณ ซึ่งเปิดใช้งานสำหรับกลุ่มผู้ใช้บางกลุ่ม แต่ก็สามารถทำได้ง่ายๆ เช่น "เปิด" และ "ปิด" เราจะจัดเก็บแฟล็กคุณลักษณะของเราโดยใช้ประเภทข้อมูลแฮชที่ Redis มีให้ ข้อมูลของเราจะมีลักษณะเหมือน JSON ด้านล่าง โดยที่ "1" ถูกเปิดใช้งาน/เปิด และ "0" ถูกปิดใช้งาน/ปิด"

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

ในการเข้าถึงและจัดการข้อมูลนี้ เราจะใช้คำสั่ง/ฟังก์ชันสี่คำสั่งจาก Redis:

  • hgetall เพื่อดึงคีย์ทั้งหมด (คุณสมบัติ) และค่า (เปิดใช้งาน/ปิดใช้งาน)
  • hset เพื่อเปิดใช้งานหรือปิดใช้งานการตั้งค่าสถานะเฉพาะ
  • hdel เพื่อลบการตั้งค่าสถานะเฉพาะ
  • hmget เพื่อรับค่าแฟล็กคุณลักษณะหลายค่าพร้อมกัน

การจัดการคุณลักษณะ

เราจะสร้างหน้าที่อยู่ที่ /features ซึ่งรับผิดชอบในการสร้างและจัดการ (เปิด/ปิด/ลบ) คุณลักษณะที่มีอยู่ เราจะลงรายละเอียดเกี่ยวกับสิ่งที่ AddFeature และ FeatureList ทำเมื่อเราพูดถึงวิธีการโหลดข้อมูลและวิธีเขียนข้อมูล

// app/routes/features.tsx
export default function Features() {
  return (
    <div>
      <h1>Features</h1>
      <AddFeature />
      <FeatureList />
    </div>
  );
}

ตัวโหลดข้อมูล

ตัวโหลดข้อมูลเป็นฟังก์ชันที่ส่งออกใน Remix ชื่อ loader ซึ่งทำงานบนเซิร์ฟเวอร์และส่งคืนข้อมูลที่มีให้สำหรับองค์ประกอบ React ของเราผ่านตะขอ

เรากำลังเริ่มต้นด้วยหน้าเพื่อสร้างและจัดการการตั้งค่าสถานะคุณลักษณะ และในกรณีนี้ เราต้องการส่งคืนคุณลักษณะทั้งหมด โดยจะส่งกลับเป็นอาร์เรย์ของคู่:

[
  ["graph", true],
  ["chart", false]
]

เริ่มต้นด้วยคำจำกัดความประเภท TypeScript จากนั้นเราจะเห็นฟังก์ชันชื่อ loadAllFeatures ที่ใช้ hgetall ฟังก์ชันจาก @upstash/redis .

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

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

const loadAllFeatures = async () => {
  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) => {
    if (a[0] > b[0]) return 1;
    if (a[0] < b[0]) return -1;
    return 0;
  });
};

loader .ที่ส่งออก ฟังก์ชั่นจะเรียก loadAllFeatures ฟังก์ชั่นส่งคืนคุณสมบัติที่จะส่งผ่านไปยังองค์ประกอบ React

export const loader: LoaderFunction = async (): Promise<LoaderData> => {
  // You would want to add authentication/authorization here
  const features = await loadAllFeatures();
  return { features };
};

เราจะพูดถึงรายละเอียดขององค์ประกอบ React นี้ในภายหลัง แต่เพื่อแสดงวิธีที่คุณเข้าถึงข้อมูลที่ส่งคืนจากฟังก์ชันตัวโหลด คุณใช้ Remix hook ชื่อ useLoaderData .

const FeatureList = () => {
  const { features } = useLoaderData<LoaderData>();

  return (
    <ul>
      {features.map(([feature, active]) => (
        <li key={feature}>{/* coming soon */}</li>
      ))}
    </ul>
  );
};

การทำงานของแบบฟอร์ม

เราได้เห็นแล้วว่าข้อมูลถูกโหลดอย่างไร แต่ในขั้นตอนนี้ เราไม่มีคุณสมบัติใดๆ ในฐานข้อมูลแฟล็กคุณลักษณะของเรา! นี่คือจุดที่การกระทำของฟอร์มเข้ามาเล่น ข้อมูลถูกประมวลผลใน Remix โดยการส่งออกฟังก์ชันชื่อ action . เหมือน loader , สิ่งนี้ทำงานบนเซิร์ฟเวอร์และโดยทั่วไปจะส่งคืน json ข้อมูลที่องค์ประกอบ React สามารถเข้าถึงได้ผ่าน hook อื่น หรือสามารถบอกให้เบราว์เซอร์ redirect ไปหน้าอื่น

action ฟังก์ชันด้านล่างจะจัดการกับการกระทำต่างๆ สี่ประเภทอย่างแท้จริง การสร้างคุณลักษณะ การเปิด/ปิดคุณลักษณะ และการลบคุณลักษณะ เราจัดการสิ่งนี้ด้วย switch คำสั่งที่เรียกใช้ฟังก์ชัน/คำสั่ง Redis ที่เหมาะสม

export const action: ActionFunction = async ({ request }) => {
  // You would want to add authentication/authorization here
  const formData = await request.formData();
  const feature = formData.get("feature") as string;
  const action = formData.get("_action") as string;

  if (!feature || feature.length === 0) {
    // This isn't currently displayed in our component
    return json({ error: "Please provide a feature" });
  }

  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");
};

คุณจะสังเกตเห็นว่าเมื่อประสบความสำเร็จ ฉันเปลี่ยนเส้นทางไปยังหน้าเดียวกับที่ผู้ใช้กำลังเปิดอยู่ สิ่งนี้จะทริกเกอร์การโหลดหน้าซ้ำ โดยเรียก loader ฟังก์ชันและอัปเดตสิ่งที่แสดงต่อผู้ใช้

ในการเพิ่มการตั้งค่าสถานะคุณลักษณะใหม่ AddFeature คอมโพเนนต์จะใช้ Remix Form องค์ประกอบที่จะส่งข้อมูลไปยังฟังก์ชันการทำงานที่เราเห็นด้านบน ฉันระบุว่าควรส่งผ่าน post วิธีการและยังให้ replace เพื่อไม่ให้เพิ่มหน้าใหม่ในประวัติของเบราว์เซอร์ทุกครั้งที่เราสร้างการตั้งค่าสถานะคุณลักษณะ

const AddFeature = () => {
  return (
    <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>
  );
};

เมื่อสร้างคุณลักษณะแล้ว เราจะต้องการแสดงการตั้งค่าสถานะปัจจุบันทั้งหมดเพื่อให้สามารถจัดการได้ อันที่จริงแล้ว แฟล็กของฟีเจอร์จะแสดงสองรูปแบบ:อันแรกสำหรับเปิด/ปิดแฟล็กของฟีเจอร์ และอีกอันสำหรับลบออก

โปรดทราบว่ามีสองฟิลด์ที่ซ่อนอยู่:_action เพื่อให้ action . ของเรา ฟังก์ชั่นรู้ว่าเรากำลังพยายามทำอะไรกับคุณลักษณะนี้และ feature ซึ่งส่งชื่อแฟล็กที่เราต้องการแก้ไข

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>
  );
};

การใช้คุณลักษณะ

เรามีการตั้งค่าสถานะคุณลักษณะในฐานข้อมูล Upstash Redis ของเรา แต่สิ่งที่ดีคือถ้าเราไม่เปิดหรือปิดฟังก์ชันการทำงานในแอปของเราตามการตั้งค่าสถานะเหล่านี้ เราจะใช้ฟังก์ชันตัวโหลดเพื่อโหลดคุณสมบัติเฉพาะจากฐานข้อมูลโดยใช้ hmget แล้วจัดการข้อมูลเล็กน้อยเพื่อให้มีโครงสร้างที่ถูกต้อง

หากเราต้องการโหลด ["chart", "graph", "fake"] ตั้งค่าสถานะ Redis จะส่งคืน ["1", "0", null] ... โปรดทราบว่าหากไม่มีแฟล็ก ค่าจะเป็น null ซึ่งฉันต้องการแสดงโดยใส่ fake ธง.

type LoaderData = {
  features: Record<string, boolean>;
};

const loadFeatures = async (keys: Array<string>) => {
  const data = await redis.hmget("features", ...keys);

  const features = keys.reduce<Record<string, boolean>>((acc, key, index) => {
    acc[key] = data[index] === "1";
    return acc;
  }, {});

  return features;
};

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

ขณะนี้ เราสามารถเข้าถึงข้อมูลที่โหลดในคอมโพเนนต์ของเราได้อีกครั้งโดยใช้ useLoaderData . ของ Remix ตะขอ. จากนั้นเลือกว่าหน้าที่การทำงานของเว็บไซต์ของเราควรเปลี่ยนแปลงอย่างไรไม่ว่าจะเปิดหรือปิดการตั้งค่าสถานะ

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>
  );
}

บทสรุป

ในบทความนี้ เราได้เห็นวิธีการใช้ Upstash Redis เพื่อสร้างระบบแฟล็กฟีเจอร์อย่างง่ายใน Remix โดยใช้ประโยชน์จากตัวโหลดข้อมูลและฟอร์มฟังก์ชันฝั่งเซิร์ฟเวอร์ สิ่งเหล่านี้ช่วยให้เรารักษาแบ็กเอนด์และฟรอนท์เอนด์ของหน้าเฉพาะไว้อย่างแนบแน่น ทำซ้ำได้อย่างรวดเร็วโดยไม่จำเป็นต้องตั้งค่า GraphQL API แยกต่างหากและแทนที่เหตุการณ์การส่งแบบฟอร์มมาตรฐานในส่วนหน้า รีมิกซ์ในขณะที่เราเห็นว่าใช้มาตรฐานเว็บเกี่ยวกับวิธีที่แบบฟอร์มส่งข้อมูลของพวกเขา