Cloudflare এর হাতেখড়ি (পর্ব ৫) – Workers দিয়ে API রুট বানানো

Cloudflare Workers দিয়ে API রুট বানানো

আগের পর্বে একটা Worker বানিয়েছিলাম, যা সবসময় একই ধরনের রেসপন্স দিত। কিন্তু বাস্তব API-তে /api/books, /api/users-এর মতো একাধিক আলাদা path থাকে, প্রতিটার কাজ আলাদা। এই পর্বে শিখব একটা Worker দিয়ে কীভাবে একাধিক রুট হ্যান্ডেল করতে হয়, HTTP মেথড (GET, POST) অনুযায়ী কাজ ভাগ করতে হয়, আর CORS-এর মতো বাস্তব সমস্যা সামলাতে হয়।

Path অনুযায়ী রুটিং

পর্ব ৪-এ new URL(request.url) ব্যবহার করেছিলাম query parameter পড়তে। এবার .pathname দিয়ে ভিন্ন path-এ ভিন্ন কাজ করব।

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    if (url.pathname === "/api/books") {
      return new Response(JSON.stringify({ books: ["অপরাজিত", "পথের পাঁচালী"] }), {
        headers: { "content-type": "application/json" },
      });
    }

    if (url.pathname === "/api/users") {
      return new Response(JSON.stringify({ users: ["আরিফ"] }), {
        headers: { "content-type": "application/json" },
      });
    }

    return new Response("পেজ পাওয়া যায়নি", { status: 404 });
  },
};

এখানে if-এর সিরিজটা আসলে একটা সাধারণ router — নির্দিষ্ট path না মিললে শেষে ৪০৪ রেসপন্স।

HTTP মেথড অনুযায়ী আলাদা লজিক

শুধু path না, একই path-এ GET আর POST-এর কাজ আলাদা হতে পারে — GET দিয়ে ডেটা আনা, POST দিয়ে নতুন ডেটা যোগ করা।

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    if (url.pathname === "/api/books" && request.method === "GET") {
      return new Response(JSON.stringify({ books: ["অপরাজিত"] }), {
        headers: { "content-type": "application/json" },
      });
    }

    if (url.pathname === "/api/books" && request.method === "POST") {
      const body = await request.json(); // পাঠানো ডেটা পড়া
      return new Response(JSON.stringify({ message: "বই যোগ হলো", received: body }), {
        status: 201,
        headers: { "content-type": "application/json" },
      });
    }

    return new Response("পেজ পাওয়া যায়নি", { status: 404 });
  },
};

request.json() দিয়ে POST রিকোয়েস্টের body পার্স করা হচ্ছে — এটা একটা Promise, তাই await লাগবে (Advanced JS-এর async/await প্যাটার্ন)।

বড় হলে — একটা রাউটার হেল্পার ফাংশন

রুট বাড়তে থাকলে একগাদা if পড়তে কষ্ট হয়। একটা ছোট রাউটার প্যাটার্ন লিখে কোড পরিষ্কার রাখা যায়।

const routes = {
  "GET /api/books": () => new Response(JSON.stringify({ books: ["অপরাজিত"] }), {
    headers: { "content-type": "application/json" },
  }),
  "GET /api/users": () => new Response(JSON.stringify({ users: ["আরিফ"] }), {
    headers: { "content-type": "application/json" },
  }),
};

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const key = `${request.method} ${url.pathname}`;
    const handler = routes[key];

    if (handler) return handler();
    return new Response("পেজ পাওয়া যায়নি", { status: 404 });
  },
};

এখানে routes একটা অবজেক্ট, যেখানে key হলো "মেথড path" আর value হলো সেই রুটের হ্যান্ডলার ফাংশন — এটা React এর হাতেখড়ি সিরিজের কোনো object-lookup প্যাটার্নের মতোই, শুধু এখানে ব্যাকএন্ড রাউটিং-এ প্রয়োগ হচ্ছে।

CORS — সমস্যাটা কী?

ধরা যাক আপনার React অ্যাপ arifstack.com-এ চলছে, আর সেখান থেকে my-worker.workers.dev-এর API কল করছেন। ব্রাউজার নিরাপত্তার কারণে এই ভিন্ন ডোমেইনের রিকোয়েস্ট ব্লক করে দেয়, যদি না সার্ভার স্পষ্টভাবে অনুমতি দেয় — এটাকেই বলে CORS (Cross-Origin Resource Sharing) সমস্যা।

// ❌ কনসোলে সাধারণত এই ধরনের এরর দেখা যায়
// Access to fetch at 'https://my-worker.workers.dev/api/books'
// from origin 'https://arifstack.com' has been blocked by CORS policy

সমাধান — CORS হেডার যোগ করা

Worker-এর রেসপন্সে কিছু নির্দিষ্ট হেডার যোগ করলেই ব্রাউজার অনুমতি দিয়ে দেয়।

const corsHeaders = {
  "Access-Control-Allow-Origin": "https://arifstack.com", // নির্দিষ্ট ডোমেইনকে অনুমতি
  "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type",
};

export default {
  async fetch(request, env, ctx) {
    // ব্রাউজার আগে একটা OPTIONS "preflight" রিকোয়েস্ট পাঠায় — এটাকে আলাদাভাবে হ্যান্ডেল করতে হয়
    if (request.method === "OPTIONS") {
      return new Response(null, { headers: corsHeaders });
    }

    const url = new URL(request.url);

    if (url.pathname === "/api/books") {
      return new Response(JSON.stringify({ books: ["অপরাজিত"] }), {
        headers: { "content-type": "application/json", ...corsHeaders },
      });
    }

    return new Response("পেজ পাওয়া যায়নি", { status: 404, headers: corsHeaders });
  },
};

গুরুত্বপূর্ণ: Access-Control-Allow-Origin-এ * (সবাইকে অনুমতি) দেওয়া সহজ মনে হলেও প্রোডাকশনে যতটা সম্ভব নির্দিষ্ট ডোমেইন বসানো ভালো, নাহলে যে কেউ আপনার API নিজের সাইটে ব্যবহার করতে পারবে।

ইন্টারেক্টিভ — Router সিমুলেটর

নিচে একটা মেথড আর path বেছে নিয়ে দেখো Worker কী রেসপন্স দেবে।

ভালো অভ্যাস

  • 404-এর জন্য সবসময় একটা fallback রাখুন — কোনো রুট না মিললে স্পষ্ট রেসপন্স দিন, নাহলে ডিবাগ করা কঠিন হয়ে যায়
  • CORS-এ নির্দিষ্ট origin ব্যবহার করুন* শুধু ডেভেলপমেন্টে, প্রোডাকশনে না
  • OPTIONS রিকোয়েস্ট আলাদা হ্যান্ডেল করুন — POST/PUT-এর মতো মেথডে ব্রাউজার আগে preflight পাঠায়, এটা মিস করলে CORS এরর আসতেই থাকবে

নিজে চেষ্টা করো

Exercise:

  1. উপরের routes অবজেক্ট প্যাটার্ন দিয়ে নিজের একটা Worker বানাও, কমপক্ষে ৩টা ভিন্ন রুট রাখো।
  2. একটা POST রুট বানাও যা request.json() দিয়ে body পড়ে সেটা ফেরত পাঠায়।
  3. নিজের একটা Pages প্রজেক্ট থেকে Worker API কল করে দেখো CORS এরর আসে কিনা, তারপর corsHeaders যোগ করে সেটা ঠিক করো।
  4. একটা অজানা path কল করে দেখো ৪০৪ রেসপন্স ঠিকমতো আসছে কিনা।

পরের পর্বে যা থাকছে

পরের পর্বে আমরা শিখব Workers + Firebase একসাথে ব্যবহার — কীভাবে একটা Worker থেকে Firebase Admin SDK দিয়ে নিরাপদে RTDB-তে ডেটা লেখা/পড়া যায়, আর সিক্রেট API কী কীভাবে নিরাপদে সংরক্ষণ করতে হয়।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম