কম্পিউটার

Blitz.js এবং Redis এর সাথে একটি করণীয় তালিকা তৈরি করা

Blitz.js হল একটি React ফ্রেমওয়ার্ক যা মূলত Next.js থেকে তৈরি করা হয়েছিল। আজ আমরা একটি Blitz.js টু-ডু অ্যাপ্লিকেশন তৈরি করব যা Upstash-এ কাজগুলি সঞ্চয় করে৷ আর কিছু না করে, চলুন শুরু করা যাক!

সেটআপ

শুরু করার জন্য আপনাকে আপনার কম্পিউটারে Blitz.js ইনস্টল করতে হবে।

NPM:

npm install -g blitz --legacy-peer-deps

সুতা:

yarn global add blitz

একটি নতুন Blitz.js অ্যাপ তৈরি করতে, blitz new ব্যবহার করুন এবং ডিরেক্টরিতে সিডি।

blitz new blitzjs-todo && cd blitzjs-todo

দারুণ, এখন আমাদের ওয়েবসাইট স্টাইল করার জন্য TailwindCSS ইনস্টল করা যাক।

blitz install tailwind

সবশেষে, Upstash API এ কল করা সহজ করতে Upstash JS SDK ইনস্টল করা যাক।

NPM:

npm i @upstash/redis

সুতা:

yarn i @upstash/redis

এই মুহুর্তে, আপনার blitz dev চালানো উচিত সবকিছু সঠিকভাবে কাজ করছে তা নিশ্চিত করতে। একটি অ্যাকাউন্ট তৈরি করার এবং সাইন ইন করার চেষ্টা করুন৷ আপনি যদি এখন পর্যন্ত সবকিছু ঠিকঠাক করে থাকেন তাহলে এটি দেখতে হবে।

Blitz.js এবং Redis এর সাথে একটি করণীয় তালিকা তৈরি করা

উপরন্তু, আপনার ফাইল গঠন এই মত হওয়া উচিত:

Blitz.js এবং Redis এর সাথে একটি করণীয় তালিকা তৈরি করা

এই UPSTASH_REDIS_REST_URL কপি করুন এবং UPSTASH_REDIS_REST_TOKEN Upstash কনসোল থেকে .env নামক ফাইলে আপাতত এটি এইরকম হওয়া উচিত:

# This env file should be checked into source control
# This is the place for default values for all environments
# Values in `.env.local` and `.env.production` will override these values

UPSTASH_REDIS_REST_URL=YOUR_URL_HERE
UPSTASH_REDIS_REST_TOKEN=YOUR_TOKEN_HERE

আমরা এখন সম্পূর্ণরূপে আমাদের Blitz.js অ্যাপ্লিকেশন সেট আপ করেছি! আসুন আমাদের করণীয় তালিকা বাস্তবায়ন শুরু করি।

বাস্তবায়ন

Blitz.js অন্তর্নির্মিত ব্যবহারকারী প্রমাণীকরণের সাথে আসে! আসুন প্রতিটি ব্যবহারকারীর জন্য ব্যক্তিগত করণীয় তালিকা তৈরি করতে এটিকে কাজে লাগাই।

প্রথমে, চলুন /lib/redis.ts-এ Upstash JS SDK আরম্ভ করি

import { Redis } from "@upstash/redis";
const redis = Redis.fromEnv();
export default redis;

আমাদের 3 তৈরি করতে হবে আমাদের করণীয় তালিকা অ্যাক্সেস করার জন্য বিভিন্ন API রুট।

app/api-এ নেভিগেট করুন এবং getall.ts নামে একটি ফাইল তৈরি করুন . একবার আপনি এটি করার পরে, নিম্নলিখিত কোডটি পেস্ট করুন:

import { BlitzApiRequest, BlitzApiResponse, getSession } from "blitz";
import redis from "../../lib/redis";

export const handler = async (req: BlitzApiRequest, res: BlitzApiResponse) => {
  const session = await getSession(req, res);
  if (!session.userId) {
    res.status(401).json({ error: `Do not tamper with this route!` });
  } else {
    await redis
      .lrange(String(session.userId), 0, 100)
      .then((data) => res.status(200).json({ data: data, success: true }))
      .catch((error) => res.status(500).json({ error: error }));
  }
};
export default handler;

আসুন ধাপে ধাপে এই API রুটটি কীভাবে কাজ করে তা দেখুন। প্রথমত, আমরা রুটে একটি অনুরোধ করি। রুটেই, আমরা যাচাই করি যে ব্যবহারকারী লগ ইন করেছেন। যদি কোন ব্যবহারকারী না থাকে, আমরা একটি "অনুমোদিত নয়" প্রতিক্রিয়া ফেরত দিই। যদি থাকে একজন ব্যবহারকারী, তারপর আমরা আমাদের Upstash Redis ডাটাবেস নিয়ে আসি যা বর্তমানে তালিকায় থাকা সমস্ত করণীয় খুঁজে পেতে। এটি প্রায় একশত করণীয় নিয়ে আসবে৷

প্রশ্ন:অপেক্ষা করুন, আমরা কীভাবে প্রথম স্থানে টু-ডস যোগ করব? উত্তর:ভাল প্রশ্ন! এর পরেরটা করা যাক!

আবার, add.ts নামে একটি নতুন ফাইলে নিম্নলিখিত কোডটি পেস্ট করুন app/api-এ .

import { BlitzApiRequest, BlitzApiResponse, getSession } from "blitz";
import redis from "../../lib/redis";
const handler = async (req: BlitzApiRequest, res: BlitzApiResponse) => {
  const session = await getSession(req, res);
  if (req.method !== "POST" || !req.body.data || !session.userId) {
    res.status(401).json({ error: `Do not tamper with this route!` });
  } else {
    let todo = encodeURI(req.body.data);
    await redis
      .lpush(String(session.userId), todo)
      .then(() => res.status(200).json({ success: true }))
      .catch(() => res.status(500).json({ error: "Error adding data." }));
  }
};
export default handler;

এই API রুটটি শেষের সাথে বেশ মিল, কিন্তু লক্ষ্য করুন যে আমরা পঞ্চম লাইনে আরও চেক যোগ করেছি। কারণ এই অনুরোধটি GET নয় অনুরোধ, বরং, এটি একটি POST অনুরোধ লক্ষ্য করুন কিভাবে আমরা তিনটি জিনিস পরীক্ষা করি প্রথমে, আমরা নিশ্চিত করি যে অনুরোধটি আসলে একটি POST অনুরোধ এরপর, আমরা নিশ্চিত করি যে req.body.data-এ JSON বা টেক্সট আছে . অবশেষে, আমরা নিশ্চিত করি যে ব্যবহারকারী লগ ইন করেছেন৷ যদি এই সমস্ত ছোট চেক পাস করা হয়, আমরা আমাদের করণীয়গুলিকে Upstash-এ আমাদের Redis তালিকায় ঠেলে দিতে পারি৷ আনার সময় কোনো ধরনের ত্রুটি থাকলে, আমরা .catch ব্যবহার করে একটি 500 ফেরত দিতে পারি .

আমাদের করণীয়গুলি সরানোর জন্য আমাদের শেষ রুটটি যোগ করতে হবে। একবার আপনি কিছু শেষ, আপনি অবশ্যই তা অতিক্রম করতে হবে! চলুন app/api/remove.ts-এ আমাদের শেষ API রুট যোগ করি . ফাইলটিতে নিম্নলিখিত কোডটি অনুলিপি করুন:

import { BlitzApiRequest, BlitzApiResponse, getSession } from "blitz";
import redis from "../../lib/redis";
const handler = async (req: BlitzApiRequest, res: BlitzApiResponse) => {
  const session = await getSession(req, res);
  if (req.method !== "POST" || !req.body.data || !session.userId) {
    res.status(401).json({ error: `Do not tamper with this route!` });
  } else {
    let todo = encodeURI(req.body.data);
    await redis
      .lrem(String(session.userId), 1, todo)
      .then(() => res.status(200).json({ success: true }))
      .catch(() => res.status(500).json({ error: "Error removing data." }));
  }
};
export default handler;

অনুরূপ কিছু লক্ষ্য করুন? কারণ এই রুটটি প্রায় add-এর মত API রুট। এখানে প্রধান পার্থক্য হল, আমরা LREM ব্যবহার করছি , LPUSH নয় , Redis থেকে একটি আইটেম সরাতে।

ফ্রন্টেন্ড তৈরি করা

শুরু করতে, চলুন app/pages/index.js-এর সবকিছু মুছে ফেলি এবং ধাপে ধাপে আমাদের করণীয় তালিকা লিখুন।

ফাইলের শীর্ষে, এই আমদানিগুলি পেস্ট করুন৷

import { Link, BlitzPage, useMutation, Routes, getAntiCSRFToken } from "blitz";
import { useRef, useEffect, useState, Suspense } from "react";
import Layout from "app/core/layouts/Layout";
import { useCurrentUser } from "app/core/hooks/useCurrentUser";
import logout from "app/auth/mutations/logout";

আমরা আমাদের করণীয় তালিকার মূল কার্যকারিতা তৈরি করতে প্রতিক্রিয়া হুক ব্যবহার করব। আসুন তালিকার কিছু মূল বৈশিষ্ট্য বাস্তবায়ন করি।

const Main = () => {
  const todoRef = useRef<HTMLInputElement>(null)
  const [todos, setTodos] = useState([])
  const currentUser = useCurrentUser()
  const [logoutMutation] = useMutation(logout)
  const handleAddTodo = async (e) => {
    e.preventDefault()
    const antiCSRFToken = await getAntiCSRFToken()
    const response = await fetch("/api/add", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "anti-csrf": antiCSRFToken,
      },
      body: JSON.stringify({ data: todoRef.current?.value }),
    })
    const data = await response.json()
    if (data.success) {
      todoRef.current!.value = ""
      fetchTodos()
    }
  }
  const handleRemoveTodo = async (id) => {
    const antiCSRFToken = await getAntiCSRFToken()
    const response = await fetch("/api/remove", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "anti-csrf": antiCSRFToken,
      },
      body: JSON.stringify({ data: id }),
    })
    const data = await response.json()
    if (data.success) {
      fetchTodos()
    }
  }
  const fetchTodos = async () => {
    const antiCSRFToken = await getAntiCSRFToken()
    const response = await fetch("/api/getall", {
      method: "GET",
      headers: {
        "anti-csrf": antiCSRFToken,
      },
    })
    const res = await response.json()
    setTodos(res.data)
  }
  useEffect(() => {
    fetchTodos()
  }, [])

  if (currentUser) {
    return (
      <>
        <button
          className="mt-4 px-2 py-1 border-2 border-black hover:bg-gray-400 mb-3"
          onClick={async () => {
            await logoutMutation()
          }}
        >
          Logout
        </button>
        <div>
          User id: <code>{currentUser.id}</code>
          <br />
          User email: <code>{currentUser.email}</code>
        </div>
        <form className="mt-2" onSubmit={handleAddTodo}>
          <p>add a todo:</p>
          <input
            ref={todoRef}
            className="w-full border-black border-2 focus:outline-none text-center"
          />
        </form>
        <div className="flex flex-col gap-2 mt-4 bg-gray-300 rounded-md">
          {(todos as string[]).map((todo: string, index: number) => (
            <div className="flex items-center p-3 rounded-md bg-gray-300" key={index}>
              <button
                onClick={() => handleRemoveTodo(todo)}
                className="flex items-center mr-4 justify-center w-5 h-5 rounded-[0.25rem] border border-solid border-gray-500 shadow-sm hover:bg-gray-700"
              ></button>

              <span>{todo}</span>
            </div>
          ))}
        </div>
      </>
    )
  } else {
    return (
      <div className="flex flex-col gap-4 text-center">
        <Link href={Routes.SignupPage()}>
          <a className="mt-4 px-2 py-1 border-2 border-black hover:bg-gray-400">
            <strong>Sign Up</strong>
          </a>
        </Link>
        <Link href={Routes.LoginPage()}>
          <a className="mt-4 px-2 py-1 border-2 border-black hover:bg-gray-400">
            <strong>Login</strong>
          </a>
        </Link>
      </div>
    )
  }
}

<Main/> উপাদান আমাদের অ্যাপ্লিকেশন মূল. এর কোডটি ঘনিষ্ঠভাবে দেখে আমরা কীভাবে এটি ব্যবহার করি তা আমাদের বলে। আমাদের কম্পোনেন্টের শীর্ষে, আমরা আমাদের অ্যাপ্লিকেশনের জন্য স্টেট শুরু করি। এছাড়াও আমরা একটি ref ঘোষণা করি আমাদের "নতুন করণীয়" ইনপুটে পরে ব্যবহারের জন্য। এছাড়াও আপনি antiCSRFToken এর ব্যবহার লক্ষ্য করতে পারেন ! Blitz.js-এর জন্য যেকোন API রুট আনার সময় এই টোকেনগুলি ব্যবহার করা প্রয়োজন যাতে কোনও ধরণের ক্ষতিকারক অভিনেতা আপনার সাইটের ক্ষতি করতে না পারে৷ আমার মতে এটা পেয়ে ভালো লাগছে!

ওয়েবসাইটে আমাদের ডেটা পরিচালনা করতে আমরা তিনটি প্রধান ফাংশন ব্যবহার করি। এই তিনটি হল:

  • handleAddTodo
  • handleRemoveTodo
  • fetchTodos

আমরা fetchTodos কল করি পৃষ্ঠাটি লোড হওয়ার সাথে সাথে, ব্যবহারকারীর এখনও সম্পূর্ণ করতে হবে এমন সমস্ত করণীয় লোড করতে। যখন একজন ব্যবহারকারী একটি করণীয় সরিয়ে দেয় বা যোগ করে তখন আমরা fetchTodos কল করি আবার ওয়েবসাইটে সেই পরিবর্তন প্রতিফলিত করতে!

যদি কোনো ব্যবহারকারী লগ ইন না করে থাকেন, তাহলে এই পৃষ্ঠাটি দেখার আগে ব্যবহারকারীকে ওয়েবসাইটে লগইন করতে বলা হবে৷

আপনি সাইন আপ করতে পারেন বা লগ ইন করতে পারেন যদি আপনার ইতিমধ্যে ওয়েবসাইটে একটি সেশন না থাকে। মনে রাখবেন, আপনি একটি অ্যাকাউন্ট ছাড়া আপনার করণীয়গুলি সংরক্ষণ করতে পারবেন না, এবং সমস্ত API রুটের জন্য আপনাকে AntiCSRFToken দিয়ে প্রমাণীকরণ করতে হবে !

কিন্তু অপেক্ষা করুন, আরও একটি গুরুত্বপূর্ণ পদক্ষেপ! আমাদের পৃষ্ঠাটি রপ্তানি করতে হবে!

const Home: BlitzPage = () => {
  return (
    <div className="flex flex-col min-h-screen items-center justify-center">
      <main>
        <div className="my-4">
          <Suspense fallback="Loading...">
            <Main />
          </Suspense>
        </div>
      </main>
    </div>
  );
};

Home.suppressFirstRenderFlicker = true;
Home.getLayout = (page) => <Layout title="Home">{page}</Layout>;

export default Home;

আপনি উপরে দেখতে পাচ্ছেন, Blitz.js একটি পদ্ধতি ব্যবহার করে নেক্সট থেকে কিছুটা ভিন্ন, তবে পদ্ধতিটি আপাতত একই। আমরা Suspense ব্যবহার করি যেটি আমরা আগে আমদানি করেছিলাম একজন ব্যবহারকারীকে দেখানোর জন্য যে অ্যাপটি লোড হচ্ছে এবং তারপর আমরা আমাদের </Main> দেখাই কম্পোনেন্ট লোডিং শেষ হওয়ার পর!

আপনার পরিবর্তনগুলি কার্যকর দেখতে, এটিকে আপনার কনসোলে আরও একবার চালান এবং ব্রাউজারে আপনার অ্যাপে নেভিগেট করুন৷

blitz dev

আপনি যদি নির্দেশাবলী অনুসরণ করেন, আপনি একবার লগইন করলে এবং কয়েকটি করণীয় যুক্ত করলে আপনার অ্যাপ্লিকেশনটি এর লাইন বরাবর কিছু দেখা উচিত:

Blitz.js এবং Redis এর সাথে একটি করণীয় তালিকা তৈরি করা

আপনি এটির পাশের বাক্সে ক্লিক করে একটি করণীয় সরাতে পারেন, এটিই removeTodo ফাংশনটি 😉।

এর জন্য

অভিনন্দন!

আমি আশা করি আপনি এই ব্লগ পোস্টটি পড়ে নতুন কিছু শিখেছেন, এবং যদি আপনি না করেন তবে মনে রাখবেন এটি আপনার দক্ষতা বাড়াতে ক্ষতি করে না! Blitz.js, Next.js থেকে দূরে ফোকাস করছে, তাই ভবিষ্যতে এটি সম্পূর্ণ ভিন্ন ফ্রেমওয়ার্ক হতে পারে, কিন্তু এখানে তাদের ওয়েবসাইটের সাথে থাকুন!

প্রকল্প উৎস :গিটহাব লিঙ্ক

ওয়ার্কিং ডেমো: ডেমো লিঙ্ক

প্রতিক্রিয়া আছে? টুইটারে @upstash অনুসরণ করতে ভুলবেন না এবং ডিসকর্ড সার্ভারে যোগ দিন!


  1. Redis সহ SvelteKit TODO অ্যাপ

  2. রেডিসের সাথে TODO অ্যাপ রিমিক্স করুন

  3. এজ ক্যাশিং সহ 5 ms গ্লোবাল রেডিস লেটেন্সি

  4. ক্লাউডফ্লেয়ার কর্মীদের সাথে রেডিস @ এজ