परिचय
Remix इस पर एक नया दृष्टिकोण है कि फुल-स्टैक React फ्रेमवर्क होने का क्या मतलब है, मौजूदा वेब मानकों पर ध्यान केंद्रित करता है और फ्रंटएंड को बैकएंड से कसकर जोड़ता है। जब आप देखते हैं कि अपने React कंपोनेंट में डेटा लोड करना या फॉर्म से सबमिट किए गए डेटा को प्रोसेस करना कितना सरल है, तो यह घनिष्ठ युग्मन ताजी हवा की सांस जैसा है।
इस लेख में हम Upstash Redis को डेटाबेस के रूप में उपयोग करके एक सरल फीचर फ्लैग प्रबंधन प्रणाली बनाकर Remix की शक्ति देखेंगे।
पूरा सोर्स कोड यहां पाया जा सकता है।
सेटअप
आप npx create-remix@latest चलाकर और अपने पसंदीदा डिप्लॉयमेंट वातावरण को चुनकर एक बिल्कुल नया Remix ऐप प्राप्त करेंगे। मैं Vercel के साथ गया लेकिन इससे इस ट्यूटोरियल के लिए कोई फर्क नहीं पड़ना चाहिए।
Upstash Redis से जुड़ने के दो तरीके हैं: पहला TCP कनेक्शन के माध्यम से है जिसमें आप किसी भी मानक Redis क्लाइंट लाइब्रेरी का उपयोग कर सकते हैं जिसका आप उपयोग करते हैं। दूसरा Upstash के REST API के माध्यम से है। हम दूसरे विकल्प के साथ जाएंगे क्योंकि यह सभी सर्वरलेस वातावरण में उपलब्ध है, जैसे कि Cloudflare Workers वातावरण जिसमें Remix को डिप्लॉय किया जा सकता है। Upstash का एक पैकेज है जो वास्तविक Redis कमांड की नकल करता है, जिससे यह जानना आसान हो जाता है कि किस फ़ंक्शन को कॉल करना है।
अब हमें अपने Upstash Redis डेटाबेस से जुड़ने के लिए आवश्यक दो पर्यावरण चर को स्टोर करने का एक तरीका चाहिए। Remix बॉक्स से बाहर development एनवी वैरिएबल सपोर्ट के साथ नहीं आता है, लेकिन इसे dotenv को डेवलपमेंट डिपेंडेंसी के रूप में जोड़कर पूरा किया जा सकता है।
npm add --save-dev dotenv
हमारी .env फाइल में (जिसे .gitignore में जोड़ा जाना चाहिए) हम Upstash Redis से जुड़ने के लिए आवश्यक दो एनवी वैरिएबल सेट कर सकते हैं। @upstash/redis पैकेज स्वचालित रूप से इनका पता लगाता है इसलिए हमारे कोड में कनेक्ट करने की कोई आवश्यकता नहीं है। ये मान Upstash डैशबोर्ड में एक नया Redis डेटाबेस बनाने के बाद पाए जा सकते हैं।
UPSTASH_REDIS_REST_URL="https://..."
UPSTASH_REDIS_REST_TOKEN="..."
हमें अपने dev स्क्रिप्ट को अपडेट करने की आवश्यकता है ताकि dotenv एनवी वैरिएबल को पिक कर सके। अन्य स्क्रिप्ट वही रह सकती हैं।
{
"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 नामक एक फ़ंक्शन देखेंगे जो @upstash/redis से hgetall फ़ंक्शन का उपयोग करता है।
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> => {
// आप यहां प्रमाणीकरण/प्राधिकरण जोड़ना चाहेंगे
const features = await loadAllFeatures();
return { features };
};
हम बाद में इस React कंपोनेंट का विवरण कवर करेंगे, लेकिन यह दिखाने के लिए कि आप लोडर फ़ंक्शन से लौटाए गए डेटा तक कैसे पहुंचते हैं, आप Remix हुक का उपयोग करते हैं जिसे useLoaderData कहा जाता है।
const FeatureList = () => {
const { features } = useLoaderData<LoaderData>();
return (
<ul>
{features.map(([feature, active]) => (
<li key={feature}>{/* जल्द ही आ रहा है */}</li>
))}
</ul>
);
};
फॉर्म एक्शन
हमने देखा कि डेटा कैसे लोड किया जाता है, लेकिन इस चरण में हमारे फीचर फ्लैग डेटाबेस में वास्तव में कोई फीचर नहीं है! यहीं फॉर्म एक्शन खेल में आते हैं। Remix में डेटा को action नामक एक निर्यातित फ़ंक्शन को निर्यात करके प्रोसेस किया जाता है। loader की तरह, यह सर्वर पर चलता है और आमतौर पर json डेटा लौटाता है जिसे React कंपोनेंट किसी अन्य हुक के माध्यम से एक्सेस कर सकता है, या यह ब्राउज़र को किसी अन्य पेज पर रिडायरेक्ट करने के लिए कह सकता है।
नीचे दिया गया action फ़ंक्शन वास्तव में चार अलग-अलग प्रकार के एक्शन को संभालता है: एक फीचर बनाना, एक फीचर को सक्षम/अक्षम करना, और एक फीचर को हटाना। हम इसे एक स्विच स्टेटमेंट के साथ संभालते हैं जो तब उपयुक्त Redis फ़ंक्शन/कमांड को कॉल करता है।
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 || feature.length === 0) {
// यह वर्तमान में हमारे कंपोनेंट में प्रदर्शित नहीं होता है
return json({ error: "कृपया एक फीचर प्रदान करें" });
}
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 };
};
अब हम Remix के useLoaderData हुक का उपयोग करके अपने कंपोनेंट में लोडेड डेटा तक पहुंच सकते हैं। फिर चुनें कि दी गई फ्लैग वर्तमान में सक्षम या अक्षम है या नहीं, इसके आधार पर हमारी वेबसाइट की कार्यक्षमता कैसे बदलनी चाहिए।
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 सेट अप करने और फ्रंटएंड पर मानक फॉर्म सबमिशन इवेंट को ओवरराइड करने की आवश्यकता के बिना तेजी से पुनरावृति करते हैं। Remix जैसा कि हमने देखा है, फॉर्म द्वारा अपना डेटा सबमिट करने के तरीके के आसपास वेब मानकों की ओर झुकता है।