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
চালানো উচিত সবকিছু সঠিকভাবে কাজ করছে তা নিশ্চিত করতে। একটি অ্যাকাউন্ট তৈরি করার এবং সাইন ইন করার চেষ্টা করুন৷ আপনি যদি এখন পর্যন্ত সবকিছু ঠিকঠাক করে থাকেন তাহলে এটি দেখতে হবে।
উপরন্তু, আপনার ফাইল গঠন এই মত হওয়া উচিত:
এই 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
আপনি যদি নির্দেশাবলী অনুসরণ করেন, আপনি একবার লগইন করলে এবং কয়েকটি করণীয় যুক্ত করলে আপনার অ্যাপ্লিকেশনটি এর লাইন বরাবর কিছু দেখা উচিত:
আপনি এটির পাশের বাক্সে ক্লিক করে একটি করণীয় সরাতে পারেন, এটিই removeTodo
ফাংশনটি 😉।
অভিনন্দন!
আমি আশা করি আপনি এই ব্লগ পোস্টটি পড়ে নতুন কিছু শিখেছেন, এবং যদি আপনি না করেন তবে মনে রাখবেন এটি আপনার দক্ষতা বাড়াতে ক্ষতি করে না! Blitz.js, Next.js থেকে দূরে ফোকাস করছে, তাই ভবিষ্যতে এটি সম্পূর্ণ ভিন্ন ফ্রেমওয়ার্ক হতে পারে, কিন্তু এখানে তাদের ওয়েবসাইটের সাথে থাকুন!
প্রকল্প উৎস :গিটহাব লিঙ্ক
ওয়ার্কিং ডেমো: ডেমো লিঙ্ক
প্রতিক্রিয়া আছে? টুইটারে @upstash অনুসরণ করতে ভুলবেন না এবং ডিসকর্ড সার্ভারে যোগ দিন!