কম্পিউটার

সার্ভারলেস রেডিস দিয়ে SvelteKit অ্যাপ্লিকেশন তৈরি করা

SvelteKit হল Svelte-এর জন্য আসন্ন ফুল-স্ট্যাক অ্যাপ্লিকেশন ফ্রেমওয়ার্ক, UI ফ্রেমওয়ার্ক যা ছোট, দ্রুত জাভাস্ক্রিপ্ট তৈরি করতে কম্পাইলের সময়ে আপনার অ্যাপ তৈরি করে। যদিও SvelteKit আপনাকে এন্ডপয়েন্ট ব্যবহার করে সার্ভার-সাইড লজিক লিখতে দেয়, আপনি কীভাবে আপনার অ্যাপ্লিকেশনের ডেটা বজায় রাখতে চান তা আপনার উপর নির্ভর করে।

এই পোস্টে, আমি দেখাব কিভাবে একটি SvelteKit অ্যাপ্লিকেশনে Redis ব্যবহার করে ডেটা সংরক্ষণ করা যায়। মুভি ডেটাবেস (TMDB) API থেকে ডেটা ব্যবহার করে আমরা মুভি API প্রতিক্রিয়াগুলি ক্যাশে করতে এবং একটি র্যান্ডম মুভি প্রদর্শন করতে Redis ব্যবহার করব৷

ডেমো সহ অনুসরণ করতে আপনার হয় একটি Redis সংযোগ স্ট্রিং প্রয়োজন বা স্থানীয়ভাবে Redis চালান। আপনার যদি ইতিমধ্যে একটি Redis উদাহরণ না থাকে, আমি Upstash সুপারিশ করি। SvelteKit এর মতো, এটি সার্ভারহীন অ্যাপ্লিকেশনের জন্য অপ্টিমাইজ করা হয়েছে এবং আপনি যদি অনেক অনুরোধ না করেন তবে এটি বিনামূল্যে (লেখার সময় ক্যাপটি 10k/দিন)। আপনি আপনার রেডিস ইনস্ট্যান্স যেখানেই পান না কেন, আপনার নিশ্চিত হওয়া উচিত যে এটি আপনার অ্যাপ্লিকেশনটি যেখানে লেটেন্সি কমাতে মোতায়েন করা হয়েছে তার কাছাকাছি অবস্থিত৷

পূর্বশর্ত

  • SvelteKit এর সাথে প্রাথমিক পরিচিতি (যেমন পৃষ্ঠা বনাম এন্ডপয়েন্ট, লোড করা, একটি প্রদত্ত পৃষ্ঠার জন্য কোয়েরি এবং প্যারামগুলি পুনরুদ্ধার করা)
  • একটি TMDB API কী এবং Redis উদাহরণ (যেমন Upstash-এ) যদি আপনি ডেমো চালাতে বা স্থাপন করতে চান

স্টার্টার ওভারভিউ

স্টার্টার রেপো ক্লোন করুন। main শাখার শেষ ফলাফল আছে, তাই initial চেকআউট করুন শাখা এই পোস্ট বরাবর অনুসরণ করুন. আপনি যদি আপনার পরিবর্তনগুলিকে ঠেলে দিতে চান, তাহলে প্রথমে রেপো ফোর্ক করুন৷

git clone https://github.com/geoffrich/movie-search-redis.git
cd movie-search-redis
git checkout initial

এটি একটি ছোট SvelteKit অ্যাপ্লিকেশন যা টিএমডিবি API ব্যবহার করে চলচ্চিত্রগুলি অনুসন্ধান এবং তাদের বিশদ দেখার অনুমতি দেয়। এটি API প্রতিক্রিয়াগুলির সাথে ইন্টারঅ্যাক্ট করা সহজ করতে TypeScript ব্যবহার করে, কিন্তু Redis বা SvelteKit ব্যবহার করার জন্য এটি প্রয়োজনীয় নয়। নিম্নলিখিত রুটগুলি ইতিমধ্যেই বিদ্যমান (src/routes-এর ফাইলগুলির সাথে সম্পর্কিত ):

  • / হোম পেজ রেন্ডার করে
  • /search অনুসন্ধান ফলাফলের একটি তালিকা রেন্ডার করে। এটি query লাগে এবং page ক্যোয়ারী প্যারাম হিসাবে, যেমন ?query=star wars&page=3 "স্টার ওয়ার্স"
  • -এর ফলাফলের তৃতীয় পৃষ্ঠা দেখায়
  • /search.json একটি সার্ভার এন্ডপয়েন্ট যা TMDB API-কে জিজ্ঞাসা করে এবং ফলাফলের তালিকা প্রদান করে। এটি /search হিসাবে একই ক্যোয়ারী প্যারামিটার লাগে
  • /movie/[id] প্রদত্ত আইডি সহ একটি চলচ্চিত্রের বিবরণ রেন্ডার করে, যেমন /movie/11
  • /movie/[id].json একটি সার্ভার এন্ডপয়েন্ট যা TMDB API থেকে মুভির বিবরণ প্রদান করে। যেহেতু টিএমডিবি প্রতিক্রিয়া পৃষ্ঠায় ব্যবহার করার চেয়ে বেশি ডেটা ফেরত দেয়, তাই আমরা ডেটার একটি উপসেট ফেরত দেওয়ার জন্য প্রতিক্রিয়াটিকে মানিয়ে নিই৷

আপনি Netlify এ চলমান ডেমো দেখতে পারেন।

মনে রাখবেন যে TMDB API কলগুলি শুধুমাত্র সার্ভারের শেষ পয়েন্টে ঘটে। এটি তাই আমাদের API কী ক্লায়েন্টের কাছে উন্মুক্ত না হয়৷

স্থানীয়ভাবে ডেমো চালানোর জন্য, একটি .env তৈরি করুন প্রকল্পের মূলে ফাইল করুন এবং আপনার TMDB API কী এবং Redis সংযোগ স্ট্রিং যোগ করুন (নীচে নমুনা)। তারপর npm install চালান নির্ভরতা ইনস্টল করতে এবং npm run dev চালান অ্যাপটি চালানোর জন্য।

TMDB_API_KEY=KEY_GOES_HERE
REDIS_CONNECTION=CONNECTION_GOES_HERE

রানটাইমে, আমরা process.env['TMDB_API_KEY'] ব্যবহার করে এই মানগুলি অ্যাক্সেস করতে পারি অথবা process.env['REDIS_CONNECTION'] . মানগুলি .env থেকে পড়া হয় hooks.ts-এ dotenv ব্যবহার করে ফাইল .

Redis-এ API প্রতিক্রিয়া ক্যাশ করা

আমরা বিদ্যমান প্রকল্পে একটি উন্নতি করতে পারি তা হল রেডিসে একটি পৃথক মুভির জন্য API প্রতিক্রিয়া ক্যাশে করা। বর্তমানে, অ্যাপটি প্রতিবার সিনেমার পৃষ্ঠা লোড করার সময় TMDB API-কে অনুরোধ করে। আমরা যখন প্রথম কোনো অনুরোধ করি, তখন আমরা এপিআই প্রতিক্রিয়া রেডিস-এ সঞ্চয় করতে পারি যাতে আমাদের TMDB থেকে ডেটার জন্য অনুরোধ করতে না হয়।

যদিও TMDB API দ্রুত এবং অগত্যা ক্যাশে করার প্রয়োজন নেই, এটি এমন APIগুলির জন্য একটি দরকারী কৌশল হতে পারে যেগুলি প্রতিক্রিয়া জানাতে কিছু সময় নেয় বা অনুরোধের সীমা থাকে৷ এপিআই যদি নিচে নেমে যায় তাহলে এটি একটি স্থিতিস্থাপকতার স্তরও প্রদান করে।

আমরা ইতিমধ্যে একটি নির্ভরতা হিসাবে ioredis যোগ করেছি। এটি নোডের জন্য একটি রেডিস ক্লায়েন্ট যা আমাদের রেডিস ডাটাবেসের সাথে যোগাযোগ করতে সাহায্য করবে৷

src/lib/redis.ts ফাইলটি তৈরি করুন . এই ফাইলটি Redis ক্লায়েন্টকে আরম্ভ করে এবং অন্যান্য ফাংশন দ্বারা ব্যবহারের জন্য এটি রপ্তানি করে। এতে কী পাওয়ার জন্য কিছু সহায়ক ফাংশনও রয়েছে। ফাইলে নিচের কোড যোগ করুন।

import Redis from "ioredis";

const connectionString = process.env["REDIS_CONNECTION"];

export const MOVIE_IDS_KEY = "movie_ids";

/** Return the key used to store movie details for a given ID in Redis */
export function getMovieKey(id): string {
  return `movie:${id}`;
}

export default connectionString ? new Redis(connectionString) : new Redis();

মনে রাখবেন যে এই বাস্তবায়ন সার্ভারহীন দৃষ্টান্ত প্রতি একটি একক Redis ক্লায়েন্ট তৈরি করে। আরেকটি বিকল্প হল অনুরোধ প্রতি একটি Redis সংযোগ তৈরি করা এবং বন্ধ করা। Upstash-এ একটি REST API রয়েছে যার জন্য আপনাকে Redis সংযোগ শুরু করার প্রয়োজন নেই। সেরা বিকল্পটি আপনার অ্যাপের লেটেন্সি এবং মেমরির প্রয়োজনীয়তার উপর নির্ভর করে।

/src/routes/movie/[id].json.ts-এ যান ফাইল Redis ক্লায়েন্ট এবং getMovieKey আমদানি করুন redis.ts থেকে ফাংশন .

import redis, { getMovieKey } from "$lib/redis";

getMovieDetailsFromApi দেখুন ফাংশন এটি চলচ্চিত্রের বিবরণ এবং ক্রেডিট পেতে TMDB API-কে কল করে এবং সেগুলি ফেরত দেয়। ডেটা ফেরত দেওয়ার আগে, আমরা এটিকে আমাদের Redis ক্যাশে সংরক্ষণ করতে চাই যাতে পরের বার আমরা API কল করার পরিবর্তে ক্যাশে করা সংস্করণটি পুনরুদ্ধার করতে পারি। একটি নতুন cacheMovieResponse যোগ করুন Redis-এ ডেটা ক্যাশে করার জন্য ফাইলের ফাংশন।

async function cacheMovieResponse(id: number, movie, credits) {
  try {
    const cache: MovieDetails = {
      movie,
      credits,
    };
    // store movie response for 24 hours
    await redis.set(getMovieKey(id), JSON.stringify(cache), "EX", 24 * 60 * 60);
  } catch (e) {
    console.log("Unable to cache", id, e);
  }
}

প্রতিটি মুভি আইডি একটি আলাদা কী এর অধীনে সংরক্ষণ করা হবে। উদাহরণস্বরূপ, যদি আমরা আইডি 11 দিয়ে মুভির তথ্য পুনরুদ্ধার করছি, তাহলে কী হবে movie:11 . redis.set-এর শেষ দুটি আর্গুমেন্ট বলুন যে আমাদের শুধুমাত্র 24 ঘন্টা (86400 সেকেন্ড) ডেটা ক্যাশে করা উচিত। আমাদের চিরতরে ডেটা ক্যাশে করা উচিত নয়—এটি ব্যবহারের শর্তাবলী লঙ্ঘন করে, এবং ডেটা শেষ পর্যন্ত বাসি হয়ে যাবে৷

মনে রাখবেন যে আপনি ক্যাশে সংরক্ষণ করার আগে JS অবজেক্টটিকে স্ট্রিংফাই করতে হবে। আমাদের এন্ডপয়েন্টকে আরও স্থিতিস্থাপক করতে আমরা এই অপারেশনের সময় নিক্ষিপ্ত কোনো ব্যতিক্রমও ধরি। যদি আমরা ডেটা ক্যাশে করতে না পারি, তাহলেও একটি অ-হ্যান্ডেল করা ব্যতিক্রম ফেলে দেওয়ার পরিবর্তে আমাদের API থেকে ডেটা ফেরত দেওয়া উচিত৷

আমরা এখন নতুন cacheMovieResponse ব্যবহার করতে পারি getMovieDetailsFromApi এর ভিতরে ফাংশন API প্রতিক্রিয়া ক্যাশে সংরক্ষণ করতে।

async function getMovieDetailsFromApi(id: number) {
  const [movieResponse, creditsResponse] = await Promise.all([
    getMovieDetails(id),
    getCredits(id),
  ]);
  if (movieResponse.ok) {
    const movie = await movieResponse.json();
    const credits = await creditsResponse.json();

    // add this line
    await cacheMovieResponse(id, movie, credits);

    return {
      movie,
      credits,
    };
  }

  return {
    status: movieResponse.status,
  };
}

এখন আমরা ক্যাশে ডেটা সংরক্ষণ করেছি, তবে আমাদের এখনও ক্যাশে করা ডেটা পুনরুদ্ধার করতে হবে। ক্যাশে থেকে মুভির বিবরণ পড়ার জন্য একটি ফাংশন যোগ করা যাক।

async function getMovieDetailsFromCache(
  id: number
): Promise<MovieDetails | Record<string, never>> {
  try {
    const cached: string = await redis.get(getMovieKey(id));
    if (cached) {
      const parsed: MovieDetails = JSON.parse(cached);
      console.log(`Found ${id} in cache`);
      return parsed;
    }
  } catch (e) {
    console.log("Unable to retrieve from cache", id, e);
  }
  return {};
}

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

অবশেষে, আমরা মূল অনুরোধ হ্যান্ডলারে আমাদের ক্যাশিং ফাংশনকে কল করতে পারি। যদি আমরা ক্যাশে ডেটা খুঁজে পাই, আমরা এখনই তা ফেরত দিই; অন্যথায়, আমরা আগের মতো API থেকে পড়ি।

export const get: RequestHandler = async function ({ params }) {
	const { id: rawId } = params;
	// validate and sanitize the input
	const id = parseInt(rawId);
	if (isNaN(id)) {
		return {
			status: 400
		};
	}

	// add these lines
	const { movie, credits } = await getMovieDetailsFromCache(id);
	if (movie && credits) {
		return {
			body: adaptResponse(movie, credits)
		};
	}

	// fallback to the API
	const result = await getMovieDetailsFromApi(id);

আপনি ডেমো রেপোতে এই শেষ পয়েন্টের জন্য চূড়ান্ত কোড দেখতে পারেন।

এই পরিবর্তনগুলির সাথে, একটি চলচ্চিত্রের পৃষ্ঠায় নেভিগেট করার চেষ্টা করুন৷ আপনি যখন পৃষ্ঠাটি রিফ্রেশ করবেন, তখন আপনি কনসোল লগে "ক্যাশে আইডি পাওয়া গেছে" দেখতে পাবেন, এটি নির্দেশ করে যে আমরা ক্যাশে থেকে সেই মুভিটি সফলভাবে সংরক্ষণ এবং পুনরুদ্ধার করেছি৷

একটি এলোমেলো চলচ্চিত্র পুনরুদ্ধার করা

Redis শুধু API প্রতিক্রিয়া ক্যাশে করার চেয়ে আরও বেশি কিছু করতে পারে। আসুন দেখি কিভাবে আমরা একটি রুট তৈরি করতে পারি যা ব্যবহারকারীকে একটি এলোমেলো চলচ্চিত্রে পুনঃনির্দেশিত করবে৷

এটি 1 থেকে 300000 এর মধ্যে একটি র্যান্ডম সংখ্যা বাছাই করা এবং এটিকে মুভি আইডি হিসাবে ব্যবহার করার মতো সহজ নয়৷ সেই পরিসরের প্রতিটি সংখ্যা একটি চলচ্চিত্রের সাথে মিলবে না-উদাহরণস্বরূপ, 1 বা 1000 এর আইডি সহ কোনো চলচ্চিত্র নেই। সর্বাধিক সম্ভাব্য আইডি কী তা ট্র্যাক করাও কঠিন হবে, কারণ এটি সর্বদা পরিবর্তিত হবে নতুন সিনেমা যোগ করা হয়। পরিবর্তে, আমরা একটি দুই ধাপ প্রক্রিয়া ব্যবহার করে এলোমেলো চলচ্চিত্র নির্বাচন করব:

  1. যখন একটি অনুসন্ধান ক্যোয়ারী সঞ্চালিত হয়, একটি রেডিস সেটে ফিরে আসা সমস্ত আইডি রাখুন৷
  2. যখন /movie/random রুট অনুরোধ করা হয়েছে, সেই সেটের একটি এলোমেলো সদস্য পুনরুদ্ধার করুন এবং সংশ্লিষ্ট চলচ্চিত্রের বিশদ পৃষ্ঠায় পুনঃনির্দেশ করুন৷

ফেরত আসা সম্ভাব্য র‍্যান্ডম মুভিগুলি ছোট শুরু হবে, কিন্তু আরও অনুসন্ধান করা হলে তা বড় হবে৷

এলোমেলো সেটটি পূরণ করতে, /src/routes/search.json.ts আপডেট করুন নিম্নলিখিত.

import type { RequestHandler } from "@sveltejs/kit";
import type { SearchResponse } from "$lib/types/tmdb";
import redis, { MOVIE_IDS_KEY } from "$lib/redis";

const VOTE_THRESHOLD = 20;

export const get: RequestHandler = async function ({ query }) {
  const searchQuery = query.get("query");
  const page = query.get("page") ?? 1;
  const response = await fetch(
    `https://api.themoviedb.org/3/search/movie?api_key=${process.env["TMDB_API_KEY"]}&page=${page}&include_adult=false&query=${searchQuery}`
  );
  const parsed: SearchResponse = await response.json();

  // add these lines
  const filteredMovies = parsed.results.filter(
    (movie) => movie.vote_count >= VOTE_THRESHOLD
  );
  if (filteredMovies.length > 0) {
    try {
      await redis.sadd(MOVIE_IDS_KEY, ...filteredMovies.map((r) => r.id));
    } catch (e) {
      console.log(e);
    }
  }

  return {
    body: parsed,
  };
};

মনে রাখবেন যে আমরা প্রতিটি যোগ করছি না সিনেমা সেটে ফিরে. আমি এমন সিনেমাগুলিকে ফিল্টার করতে বেছে নিয়েছি যেগুলিতে বেশি ভোট নেই, যেহেতু সেই সিনেমাগুলি ব্যবহারকারীদের দ্বারা যাচাই করার সম্ভাবনা কম। আপনি VOTE_THRESHOLD সামঞ্জস্য করতে পারেন আপনার পছন্দ অনুযায়ী।

এই পরিবর্তনের সাথে, মুভিগুলির জন্য অনুসন্ধান করা মুভি আইডিগুলির সেটগুলি পূরণ করতে শুরু করবে৷ সেটে কিছু আইডি যোগ করতে কিছু অনুসন্ধান করুন। যেমন:

  • স্টার ওয়ার্স
  • সিংহ রাজা
  • স্পাইডার ম্যান

কয়েকটি অনুসন্ধান করার পরে, আপনার রেডিসে সংরক্ষিত র্যান্ডম আইডিগুলির একটি সেট থাকা উচিত। চলুন /movie/random-এর জন্য রুট তৈরি করি শেষবিন্দু এবং পৃষ্ঠা।

src/routes/movie/random.json.ts

import type { RequestHandler } from "@sveltejs/kit";
import redis, { MOVIE_IDS_KEY } from "$lib/redis";

export const get: RequestHandler = async function () {
  const randomId = await redis.srandmember(MOVIE_IDS_KEY);
  return {
    body: randomId,
  };
};

এই সার্ভার এন্ডপয়েন্ট মুভি আইডি সেট থেকে একটি র্যান্ডম আইডি বাছাই করতে SRANDMEMBER ব্যবহার করে৷

src/routes/movie/random.svelte

<script context="module" lang="ts">
  import type { Load } from "@sveltejs/kit";

  export const load: Load = async function ({ fetch }) {
    const result = await fetch(`/movie/random.json`);
    if (result.ok) {
      const id = await result.json();
      return {
        redirect: `/movie/${id}`,
        status: 303,
      };
    }

    return {
      status: result.status,
      error: new Error("Could not retrieve random id"),
    };
  };
</script>

সংশ্লিষ্ট Svelte পৃষ্ঠার শুধুমাত্র একটি লোড ফাংশন প্রয়োজন কারণ এটি কোনো UI রেন্ডার করে না। এটি সার্ভারের এন্ডপয়েন্টকে বলে যেটি আমরা এইমাত্র যোগ করেছি এবং সংশ্লিষ্ট মুভি পৃষ্ঠায় পুনঃনির্দেশ করি৷

এখানেই শেষ এটা পেতে ওখানে যাও! এখন, https://localhost:3000/movie/random-এ নেভিগেট করুন। আপনার সম্পাদিত পূর্ববর্তী অনুসন্ধানগুলি থেকে আপনাকে স্বয়ংক্রিয়ভাবে একটি এলোমেলো চলচ্চিত্রে পুনঃনির্দেশিত করা উচিত। এই রুটে অ্যাক্সেস করা সহজ করতে, আপনি এটিকে /src/routes/__layout.svelte-এ নেভিগেশনে যোগ করতে পারেন

<header>
  <nav>
    <a href="/">Search</a>
    <a href="/movie/random">Random</a>
  </nav>
</header>

আপনি লাইভ ডেমোতে এটি কাজ করতে দেখতে পারেন৷

র্যাপিং আপ

Redis ব্যবহার করার আরও অনেক উপায় আছে, কিন্তু আমি আশা করি এই পোস্টটি আপনাকে একটি SvelteKit অ্যাপে Redis সংহত করার মূল বিষয়গুলি সম্পর্কে একটি ভাল ধারণা দিয়েছে। আপনি GitHub-এ চূড়ান্ত কোড এবং Netlify-এ লাইভ ডেমো দেখতে পারেন।

কোনো প্রশ্ন আছে কি? টুইটারে যোগাযোগ করুন। আপনি আমার ব্লগে Svelte সম্পর্কে আমার অন্যান্য লেখা খুঁজে পেতে পারেন।


  1. ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 2

  2. ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 1

  3. সার্ভারলেস রেডিসে পাইপলাইন REST API

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