সার্ভারহীন ওয়েব এপিআই ওয়ার্কশপ

1. ওভারভিউ

এই কোডল্যাবের লক্ষ্য হল Google ক্লাউড প্ল্যাটফর্ম দ্বারা অফার করা "সার্ভারহীন" পরিষেবাগুলির সাথে অভিজ্ঞতা অর্জন করা:

  • ক্লাউড ফাংশন - ফাংশনের আকারে ব্যবসায়িক যুক্তির ছোট ইউনিট স্থাপন করতে, যা বিভিন্ন ইভেন্টে প্রতিক্রিয়া দেখায় (পাব/সাব বার্তা, ক্লাউড স্টোরেজে নতুন ফাইল, HTTP অনুরোধ এবং আরও অনেক কিছু),
  • অ্যাপ ইঞ্জিন — দ্রুত স্কেল আপ এবং ডাউন ক্ষমতা সহ ওয়েব অ্যাপ, ওয়েব এপিআই, মোবাইল ব্যাকএন্ড, স্ট্যাটিক সম্পদ স্থাপন এবং পরিবেশন করতে,
  • ক্লাউড রান — যে কোনো ভাষা, রানটাইম বা লাইব্রেরি থাকতে পারে এমন কন্টেইনার স্থাপন এবং স্কেল করতে।

এবং ওয়েব এবং REST API গুলি স্থাপন এবং স্কেল করার জন্য কীভাবে সেই সার্ভারবিহীন পরিষেবাগুলির সুবিধা নেওয়া যায় তা আবিষ্কার করতে, পাশাপাশি কিছু ভাল RESTful ডিজাইন নীতিগুলিও দেখছেন৷

এই কর্মশালায়, আমরা একটি বুকশেলফ এক্সপ্লোরার তৈরি করব যার মধ্যে রয়েছে:

  • একটি ক্লাউড ফাংশন: আমাদের লাইব্রেরিতে উপলব্ধ বইগুলির প্রাথমিক ডেটাসেট আমদানি করতে, ক্লাউড ফায়ারস্টোর ডকুমেন্ট ডাটাবেসে,
  • একটি ক্লাউড রান কন্টেইনার: যা আমাদের ডাটাবেসের বিষয়বস্তুর উপর একটি REST API প্রকাশ করবে,
  • একটি অ্যাপ ইঞ্জিন ওয়েব ফ্রন্টএন্ড: আমাদের REST API কল করে বইয়ের তালিকা ব্রাউজ করতে।

এই কোডল্যাবের শেষে ওয়েব ফ্রন্টএন্ড কেমন দেখাবে তা এখানে:

705e014da0ca5e90.png

আপনি কি শিখবেন

  • ক্লাউড ফাংশন
  • ক্লাউড ফায়ারস্টোর
  • ক্লাউড রান
  • অ্যাপ ইঞ্জিন

2. সেটআপ এবং প্রয়োজনীয়তা

স্ব-গতিসম্পন্ন পরিবেশ সেটআপ

  1. Google ক্লাউড কনসোলে সাইন-ইন করুন এবং একটি নতুন প্রকল্প তৈরি করুন বা বিদ্যমান একটি পুনরায় ব্যবহার করুন৷ আপনার যদি ইতিমধ্যেই একটি Gmail বা Google Workspace অ্যাকাউন্ট না থাকে, তাহলে আপনাকে অবশ্যই একটি তৈরি করতে হবে।

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • প্রকল্পের নাম এই প্রকল্পের অংশগ্রহণকারীদের জন্য প্রদর্শনের নাম। এটি একটি অক্ষর স্ট্রিং যা Google API দ্বারা ব্যবহৃত হয় না। আপনি সবসময় এটি আপডেট করতে পারেন.
  • প্রোজেক্ট আইডি সমস্ত Google ক্লাউড প্রোজেক্ট জুড়ে অনন্য এবং অপরিবর্তনীয় (সেট করার পরে পরিবর্তন করা যাবে না)। ক্লাউড কনসোল স্বয়ংক্রিয়ভাবে একটি অনন্য স্ট্রিং তৈরি করে; সাধারণত আপনি এটা কি যত্ন না. বেশিরভাগ কোডল্যাবে, আপনাকে আপনার প্রকল্প আইডি উল্লেখ করতে হবে (সাধারণত PROJECT_ID হিসাবে চিহ্নিত)। আপনি যদি জেনারেট করা আইডি পছন্দ না করেন, তাহলে আপনি অন্য একটি এলোমেলো আইডি তৈরি করতে পারেন। বিকল্পভাবে, আপনি নিজের চেষ্টা করতে পারেন, এবং এটি উপলব্ধ কিনা দেখতে পারেন। এই ধাপের পরে এটি পরিবর্তন করা যাবে না এবং প্রকল্পের সময়কালের জন্য থাকে।
  • আপনার তথ্যের জন্য, একটি তৃতীয় মান আছে, একটি প্রকল্প নম্বর , যা কিছু API ব্যবহার করে। ডকুমেন্টেশনে এই তিনটি মান সম্পর্কে আরও জানুন।
  1. এরপরে, ক্লাউড রিসোর্স/এপিআই ব্যবহার করতে আপনাকে ক্লাউড কনসোলে বিলিং সক্ষম করতে হবে। এই কোডল্যাবের মাধ্যমে চালানোর জন্য খুব বেশি খরচ হবে না, যদি কিছু হয়। এই টিউটোরিয়ালের বাইরে বিলিং এড়াতে সংস্থানগুলি বন্ধ করতে, আপনি আপনার তৈরি করা সংস্থানগুলি মুছতে বা প্রকল্প মুছতে পারেন। নতুন Google ক্লাউড ব্যবহারকারীরা $300 USD বিনামূল্যের ট্রায়াল প্রোগ্রামের জন্য যোগ্য৷

ক্লাউড শেল শুরু করুন

যদিও Google ক্লাউড আপনার ল্যাপটপ থেকে দূরবর্তীভাবে পরিচালিত হতে পারে, এই কোডল্যাবে আপনি Google ক্লাউড শেল ব্যবহার করবেন, একটি কমান্ড লাইন পরিবেশ যা ক্লাউডে চলছে।

Google ক্লাউড কনসোল থেকে, উপরের ডানদিকে টুলবারে ক্লাউড শেল আইকনে ক্লিক করুন:

84688aa223b1c3a2.png

পরিবেশের ব্যবস্থা করতে এবং সংযোগ করতে এটি শুধুমাত্র কয়েক মুহূর্ত নিতে হবে। এটি সমাপ্ত হলে, আপনি এই মত কিছু দেখতে হবে:

320e18fedb7fbe0.png

এই ভার্চুয়াল মেশিনটি আপনার প্রয়োজনীয় সমস্ত ডেভেলপমেন্ট টুল দিয়ে লোড করা হয়েছে। এটি একটি ক্রমাগত 5GB হোম ডিরেক্টরি অফার করে এবং Google ক্লাউডে চলে, যা নেটওয়ার্ক কর্মক্ষমতা এবং প্রমাণীকরণকে ব্যাপকভাবে উন্নত করে। এই কোডল্যাবে আপনার সমস্ত কাজ একটি ব্রাউজারে করা যেতে পারে। আপনার কিছু ইন্সটল করার দরকার নেই।

3. পরিবেশ প্রস্তুত করুন এবং ক্লাউড এপিআই সক্ষম করুন৷

এই প্রকল্প জুড়ে আমাদের প্রয়োজনীয় বিভিন্ন পরিষেবা ব্যবহার করার জন্য, আমরা কয়েকটি API সক্রিয় করব। আমরা ক্লাউড শেলে নিম্নলিখিত কমান্ডটি চালু করে তা করব:

$ gcloud services enable \
      appengine.googleapis.com \
      cloudbuild.googleapis.com \
      cloudfunctions.googleapis.com \
      compute.googleapis.com \
      firestore.googleapis.com \
      run.googleapis.com

কিছু সময় পরে, আপনি সফলভাবে অপারেশন শেষ দেখতে হবে:

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

আমরা একটি এনভায়রনমেন্ট ভেরিয়েবল সেটআপ করব যা আমাদের পথ ধরে প্রয়োজন হবে: ক্লাউড অঞ্চল যেখানে আমরা আমাদের ফাংশন, অ্যাপ এবং কন্টেইনার স্থাপন করব:

$ export REGION=europe-west3

যেহেতু আমরা ক্লাউড ফায়ারস্টোর ডাটাবেসে ডেটা সংরক্ষণ করব, আমাদের ডাটাবেস তৈরি করতে হবে:

$ gcloud app create --region=${REGION}
$ gcloud firestore databases create --location=${REGION}

পরবর্তীতে এই কোডল্যাবে, REST API প্রয়োগ করার সময়, আমাদের ডেটা বাছাই এবং ফিল্টার করতে হবে। সেই উদ্দেশ্যে, আমরা তিনটি সূচক তৈরি করব:

$ gcloud firestore indexes composite create --collection-group=books \
      --field-config field-path=language,order=ascending \
      --field-config field-path=updated,order=descending 

$ gcloud firestore indexes composite create --collection-group=books \
      --field-config field-path=author,order=ascending \
      --field-config field-path=updated,order=descending 

এই 3টি সূচী একটি আপডেট ফিল্ডের মাধ্যমে সংগ্রহে ক্রম বজায় রাখার সময় লেখক বা ভাষা দ্বারা করা অনুসন্ধানগুলির সাথে মিলে যায়৷

4. কোড পান

নিম্নলিখিত Github সংগ্রহস্থল থেকে কোড পান:

$ git clone https://github.com/glaforge/serverless-web-apis

অ্যাপ্লিকেশন কোড Node.JS ব্যবহার করে লেখা হয়।

আপনার কাছে নিম্নলিখিত ফোল্ডার কাঠামো থাকবে যা এই ল্যাবের জন্য প্রাসঙ্গিক:

serverless-web-apis
 |
 ├── data
 |   ├── books.json
 |
 ├── function-import
 |   ├── index.js
 |   ├── package.json
 |
 ├── run-crud
 |   ├── index.js
 |   ├── package.json
 |   ├── Dockerfile
 |
 ├── appengine-frontend
     ├── public
     |   ├── css/style.css
     |   ├── html/index.html
     |   ├── js/app.js
     ├── index.js
     ├── package.json
     ├── app.yaml

এই প্রাসঙ্গিক ফোল্ডার:

  • data — এই ফোল্ডারে 100টি বইয়ের তালিকার নমুনা ডেটা রয়েছে।
  • function-import — এই ফাংশনটি নমুনা ডেটা ইম্পোর্ট করার জন্য একটি এন্ডপয়েন্ট অফার করবে।
  • run-crud — এই ধারকটি ক্লাউড ফায়ারস্টোরে সংরক্ষিত বইয়ের ডেটা অ্যাক্সেস করতে একটি ওয়েব API প্রকাশ করবে।
  • appengine-frontend — এই অ্যাপ ইঞ্জিন ওয়েব অ্যাপ্লিকেশনটি বইয়ের তালিকা ব্রাউজ করার জন্য একটি সাধারণ পঠনযোগ্য ফ্রন্টএন্ড প্রদর্শন করবে।

5. নমুনা বই লাইব্রেরি ডেটা

ডেটা ফোল্ডারে, আমাদের কাছে একটি books.json ফাইল রয়েছে যাতে একশত বইয়ের তালিকা রয়েছে, সম্ভবত পড়ার যোগ্য। এই JSON নথিটি JSON অবজেক্ট ধারণকারী একটি অ্যারে। ক্লাউড ফাংশনের মাধ্যমে আমরা যে ডেটা গ্রহণ করব তার আকৃতি দেখে নেওয়া যাক:

[
  {
    "isbn": "9780435272463",
    "author": "Chinua Achebe",
    "language": "English",
    "pages": 209,
    "title": "Things Fall Apart",
    "year": 1958
  },
  {
    "isbn": "9781414251196",
    "author": "Hans Christian Andersen",
    "language": "Danish",
    "pages": 784,
    "title": "Fairy tales",
    "year": 1836
  },
  ...
]

এই অ্যারেতে আমাদের সমস্ত বই এন্ট্রিতে নিম্নলিখিত তথ্য রয়েছে:

  • isbn — ISBN-13 কোড বইটিকে চিহ্নিত করে।
  • author - বইটির লেখকের নাম।
  • language - কথ্য ভাষা যেখানে বইটি লেখা হয়েছে।
  • pages - বইয়ের পৃষ্ঠাগুলির সংখ্যা।
  • title - বইটির শিরোনাম।
  • year — যে বছর বইটি প্রকাশিত হয়েছিল।

6. নমুনা বই ডেটা আমদানি করার জন্য একটি ফাংশন এন্ডপয়েন্ট

এই প্রথম বিভাগে, আমরা শেষ পয়েন্টটি বাস্তবায়ন করব যা নমুনা বই ডেটা আমদানি করতে ব্যবহৃত হবে। আমরা এই উদ্দেশ্যে ক্লাউড ফাংশন ব্যবহার করব।

কোড অন্বেষণ

package.json ফাইলটি দেখে শুরু করা যাক:

{
    "name": "function-import",
    "description": "Import sample book data",
    "license": "Apache-2.0",
    "dependencies": {
        "@google-cloud/firestore": "^4.9.9"
    },
    "devDependencies": {
        "@google-cloud/functions-framework": "^3.1.0"
    },
    "scripts": {
        "start": "npx @google-cloud/functions-framework --target=parseBooks"
    }
}

রানটাইম নির্ভরতায়, ডাটাবেস অ্যাক্সেস করতে এবং আমাদের বইয়ের ডেটা সঞ্চয় করার জন্য আমাদের শুধুমাত্র @google-cloud/firestore NPM মডিউল প্রয়োজন। হুডের অধীনে, ক্লাউড ফাংশন রানটাইম এক্সপ্রেস ওয়েব ফ্রেমওয়ার্কও সরবরাহ করে, তাই আমাদের এটিকে নির্ভরতা হিসাবে ঘোষণা করার দরকার নেই।

উন্নয়ন নির্ভরতায়, আমরা ফাংশন ফ্রেমওয়ার্ক ( @google-cloud/functions-framework ) ঘোষণা করি, যা আপনার ফাংশনগুলিকে আহ্বান করতে ব্যবহৃত রানটাইম ফ্রেমওয়ার্ক। এটি একটি ওপেন সোর্স ফ্রেমওয়ার্ক যা আপনি আপনার মেশিনে স্থানীয়ভাবে ব্যবহার করতে পারেন (আমাদের ক্ষেত্রে, ক্লাউড শেলের ভিতরে) প্রতিবার যখন আপনি পরিবর্তন করবেন তখন মোতায়েন না করে ফাংশন চালানোর জন্য, এইভাবে উন্নয়ন প্রতিক্রিয়া লুপকে উন্নত করে৷

নির্ভরতাগুলি ইনস্টল করতে, install কমান্ডটি ব্যবহার করুন:

$ npm install

start স্ক্রিপ্ট আপনাকে একটি কমান্ড দিতে ফাংশন ফ্রেমওয়ার্ক ব্যবহার করে যা আপনি নিম্নলিখিত নির্দেশের সাথে স্থানীয়ভাবে ফাংশন চালানোর জন্য ব্যবহার করতে পারেন:

$ npm start

আপনি ফাংশনের সাথে ইন্টারঅ্যাক্ট করার জন্য HTTP GET অনুরোধের জন্য কার্ল বা সম্ভাব্য ক্লাউড শেল ওয়েব প্রিভিউ ব্যবহার করতে পারেন।

এখন আমাদের বই ডেটা ইম্পোর্ট ফাংশনের যুক্তিযুক্ত index.js ফাইলটি দেখে নেওয়া যাক:

const Firestore = require('@google-cloud/firestore');
const firestore = new Firestore();
const bookStore = firestore.collection('books');

আমরা ফায়ারস্টোর মডিউলটি চালু করি এবং বই সংগ্রহের দিকে নির্দেশ করি (রিলেশনাল ডাটাবেসের একটি টেবিলের মতো)।

functions.http('parseBooks', async (req, resp) => {
    if (req.method !== "POST") {
        resp.status(405).send({error: "Only method POST allowed"});
        return;
    }
    if (req.headers['content-type'] !== "application/json") {
        resp.status(406).send({error: "Only application/json accepted"});
        return;
    }
    ... 
})

আমরা parseBooks জাভাস্ক্রিপ্ট ফাংশন রপ্তানি করছি। এটি এমন ফাংশন যা আমরা ঘোষণা করব যখন আমরা এটিকে পরে স্থাপন করব।

পরের কয়েকটি নির্দেশাবলী পরীক্ষা করছে যে:

  • আমরা শুধুমাত্র HTTP POST অনুরোধগুলি গ্রহণ করছি, এবং অন্যথায় অন্যান্য HTTP পদ্ধতিগুলি অনুমোদিত নয় তা নির্দেশ করার জন্য একটি 405 স্ট্যাটাস কোড ফেরত দিই৷
  • আমরা শুধুমাত্র application/json পেলোড গ্রহণ করছি, এবং অন্যথায় এটি একটি গ্রহণযোগ্য পেলোড বিন্যাস নয় তা নির্দেশ করার জন্য একটি 406 স্ট্যাটাস কোড পাঠান।
    const books = req.body;

    const writeBatch = firestore.batch();

    for (const book of books) {
        const doc = bookStore.doc(book.isbn);
        writeBatch.set(doc, {
            title: book.title,
            author: book.author,
            language: book.language,
            pages: book.pages,
            year: book.year,
            updated: Firestore.Timestamp.now()
        });
    }

তারপরে, আমরা অনুরোধের body মাধ্যমে JSON পেলোড পুনরুদ্ধার করতে পারি। আমরা একটি ফায়ারস্টোর ব্যাচ অপারেশন প্রস্তুত করছি, সমস্ত বই বাল্কে সংরক্ষণ করতে। আমরা isbn , title , author , language , pages এবং year ক্ষেত্রগুলির মধ্য দিয়ে গিয়ে বইয়ের বিশদ সমন্বিত JSON অ্যারের উপর পুনরাবৃত্তি করি৷ বইটির ISBN কোড এর প্রাথমিক কী বা শনাক্তকারী হিসেবে কাজ করবে।

    try {
        await writeBatch.commit();
        console.log("Saved books in Firestore");
    } catch (e) {
        console.error("Error saving books:", e);
        resp.status(400).send({error: "Error saving books"});
        return;
    };

    resp.status(202).send({status: "OK"});

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

আমদানি ফাংশন চালানো এবং পরীক্ষা করা

কোডটি চালানোর আগে, আমরা এর সাথে নির্ভরতাগুলি ইনস্টল করব:

$ npm install

স্থানীয়ভাবে ফাংশন চালানোর জন্য, ফাংশন ফ্রেমওয়ার্ককে ধন্যবাদ, আমরা package.json এ সংজ্ঞায়িত start স্ক্রিপ্ট কমান্ডটি ব্যবহার করব।

$ npm start

> start
> npx @google-cloud/functions-framework --target=parseBooks

Serving function...
Function: parseBooks
URL: http://localhost:8080/

আপনার স্থানীয় ফাংশনে একটি HTTP POST অনুরোধ পাঠাতে, আপনি চালাতে পারেন:

$ curl -d "@../data/books.json" \
       -H "Content-Type: application/json" \
       http://localhost:8080/

এই কমান্ডটি চালু করার সময়, আপনি নিম্নলিখিত আউটপুট দেখতে পাবেন, ফাংশনটি স্থানীয়ভাবে চলছে তা নিশ্চিত করে:

{"status":"OK"}

এছাড়াও আপনি ক্লাউড কনসোল UI-তে যেতে পারেন যে ডেটা প্রকৃতপক্ষে Firestore-এ সংরক্ষিত আছে কিনা তা পরীক্ষা করতে:

409982568cebdbf8.png

উপরের স্ক্রিনশটে, আমরা তৈরি করা books সংগ্রহ, বইয়ের আইএসবিএন কোড দ্বারা চিহ্নিত বই নথির তালিকা এবং ডানদিকে সেই নির্দিষ্ট বইয়ের এন্ট্রির বিবরণ দেখতে পাচ্ছি।

ক্লাউডে ফাংশন স্থাপন করা হচ্ছে

ক্লাউড ফাংশনে ফাংশন স্থাপন করতে, আমরা function-import ডিরেক্টরিতে নিম্নলিখিত কমান্ডটি ব্যবহার করব:

$ gcloud functions deploy bulk-import \
         --gen2 \
         --trigger-http \
         --runtime=nodejs20 \
         --allow-unauthenticated \
         --max-instances=30
         --region=${REGION} \
         --source=. \
         --entry-point=parseBooks

আমরা bulk-import প্রতীকী নাম দিয়ে ফাংশনটি স্থাপন করি। এই ফাংশনটি HTTP অনুরোধের মাধ্যমে ট্রিগার করা হয়। আমরা Node.JS 20 রানটাইম ব্যবহার করি। আমরা সর্বজনীনভাবে ফাংশন স্থাপন করি (আদর্শভাবে, আমাদের সেই শেষ পয়েন্টটি সুরক্ষিত করা উচিত)। আমরা সেই অঞ্চলটি নির্দিষ্ট করি যেখানে আমরা ফাংশনটি থাকতে চাই। এবং আমরা স্থানীয় ডিরেক্টরির উত্সগুলিতে নির্দেশ করি এবং এন্ট্রি পয়েন্ট হিসাবে parseBooks (রপ্তানি করা জাভাস্ক্রিপ্ট ফাংশন) ব্যবহার করি।

কয়েক মিনিট বা তার কম পরে, ফাংশনটি ক্লাউডে স্থাপন করা হয়। ক্লাউড কনসোল UI-তে, আপনি ফাংশনটি উপস্থিত দেখতে পাবেন:

c910875d4dc0aaa8.png

ডিপ্লয়মেন্ট আউটপুটে, আপনি আপনার ফাংশনের URL দেখতে সক্ষম হবেন, যা একটি নির্দিষ্ট নামকরণের নিয়ম অনুসরণ করে ( https://${REGION}-${GOOGLE_CLOUD_PROJECT}.cloudfunctions.net/${FUNCTION_NAME} ), এবং এর অবশ্যই, আপনি ক্লাউড কনসোল UI-তে, ট্রিগার ট্যাবে এই HTTP ট্রিগার URLটি খুঁজে পেতে পারেন:

380ffc46eb56441e.png

আপনি gcloud এর সাথে কমান্ড-লাইনের মাধ্যমে URL পুনরুদ্ধার করতে পারেন:

$ export BULK_IMPORT_URL=$(gcloud functions describe bulk-import \
                                  --region=$REGION \
                                  --format 'value(httpsTrigger.url)')
$ echo $BULK_IMPORT_URL

আসুন এটিকে BULK_IMPORT_URL এনভায়রনমেন্ট ভেরিয়েবলে সংরক্ষণ করি, যাতে আমরা আমাদের স্থাপন করা ফাংশন পরীক্ষা করার জন্য এটিকে পুনরায় ব্যবহার করতে পারি।

স্থাপন করা ফাংশন পরীক্ষা করা হচ্ছে

স্থানীয়ভাবে চলমান ফাংশন পরীক্ষা করার জন্য আমরা আগে ব্যবহার করা অনুরূপ কার্ল কমান্ডের সাহায্যে, আমরা স্থাপন করা ফাংশন পরীক্ষা করব। একমাত্র পরিবর্তন হবে URL:

$ curl -d "@../data/books.json" \
       -H "Content-Type: application/json" \
       $BULK_IMPORT_URL

আবার, সফল হলে, এটি নিম্নলিখিত আউটপুট ফিরিয়ে দিতে হবে:

{"status":"OK"}

এখন যেহেতু আমাদের আমদানি ফাংশন স্থাপন করা হয়েছে এবং প্রস্তুত, যে আমরা আমাদের নমুনা ডেটা আপলোড করেছি, এই ডেটাসেটটি উন্মুক্ত করার জন্য REST API বিকাশ করার সময় এসেছে৷

7. REST API চুক্তি

যদিও আমরা একটি API চুক্তি সংজ্ঞায়িত করছি না, উদাহরণস্বরূপ, Open API স্পেসিফিকেশন ব্যবহার করে, আমরা আমাদের REST API-এর বিভিন্ন প্রান্তের দিকে নজর দিতে যাচ্ছি।

API এক্সচেঞ্জ বুক JSON অবজেক্ট, যার মধ্যে রয়েছে:

  • isbn (ঐচ্ছিক) — একটি 13-অক্ষরের String একটি বৈধ ISBN কোড প্রতিনিধিত্ব করে,
  • author — বইটির লেখকের নামের প্রতিনিধিত্বকারী একটি অ-খালি String ,
  • language - একটি অ-খালি String যাতে বইটি যে ভাষায় লেখা হয়েছিল,
  • pages - বইয়ের পৃষ্ঠা সংখ্যার জন্য একটি ইতিবাচক Integer ,
  • title — বইয়ের শিরোনাম সহ একটি অ-খালি String ,
  • year — বই প্রকাশের বছরের জন্য একটি Integer মান।

বই পেলোডের উদাহরণ:

{
    "isbn": "9780435272463",
    "author": "Chinua Achebe",
    "language": "English",
    "pages": 209,
    "title": "Things Fall Apart",
    "year": 1958
  }

/বই পান

সমস্ত বইয়ের তালিকা পান, সম্ভাব্য লেখক এবং/অথবা ভাষা দ্বারা ফিল্টার করা, এবং একবারে 10টি ফলাফলের উইন্ডো দ্বারা পৃষ্ঠাবিন্যাস করা।

বডি পেলোড: কোনোটিই নয়।

ক্যোয়ারী প্যারামিটার:

  • author (ঐচ্ছিক) — লেখক দ্বারা বইয়ের তালিকা ফিল্টার করে,
  • language (ঐচ্ছিক) — ভাষা অনুসারে বইয়ের তালিকা ফিল্টার করে,
  • page (ঐচ্ছিক, ডিফল্ট = 0) — ফলাফলের পৃষ্ঠার র‌্যাঙ্ক নির্দেশ করে যা ফেরত দিতে হবে।

রিটার্ন: বই অবজেক্টের একটি JSON অ্যারে।

স্ট্যাটাস কোড:

  • 200 — যখন অনুরোধটি বইয়ের তালিকা আনতে সফল হয়,
  • 400 - যদি একটি ত্রুটি ঘটে।

POST /books এবং POST /books/{isbn}

একটি নতুন বই পেলোড পোস্ট করুন, হয় একটি isbn পাথ প্যারামিটার সহ (যে ক্ষেত্রে বইয়ের পেলোডে isbn কোডের প্রয়োজন হয় না) বা ছাড়া (যে ক্ষেত্রে isbn কোড অবশ্যই বইয়ের পেলোডে উপস্থিত থাকতে হবে)

বডি পেলোড: একটি বইয়ের বস্তু।

ক্যোয়ারী প্যারামিটার: কোনোটিই নয়।

রিটার্ন: কিছুই না।

স্ট্যাটাস কোড:

  • 201 — যখন বইটি সফলভাবে সংরক্ষণ করা হয়,
  • 406 — যদি isbn কোডটি অবৈধ হয়,
  • 400 - যদি একটি ত্রুটি ঘটে।

পান /বই/{isbn}

লাইব্রেরি থেকে একটি বই পুনরুদ্ধার করে, এটির isbn কোড দ্বারা চিহ্নিত, একটি পাথ প্যারামিটার হিসাবে পাস করা হয়েছে।

বডি পেলোড: কোনোটিই নয়।

ক্যোয়ারী প্যারামিটার: কোনোটিই নয়।

রিটার্নস: একটি বই JSON অবজেক্ট, অথবা যদি বইটির অস্তিত্ব না থাকে তাহলে একটি ত্রুটি বস্তু।

স্ট্যাটাস কোড:

  • 200 — বইটি ডাটাবেসে পাওয়া গেলে,
  • 400 - যদি একটি ত্রুটি ঘটে,
  • 404 — যদি বইটি পাওয়া না যায়,
  • 406 — যদি isbn কোডটি অবৈধ হয়।

রাখুন /বই/{isbn}

একটি বিদ্যমান বই আপডেট করে, যা এর isbn দ্বারা চিহ্নিত পাথ প্যারামিটার হিসাবে পাস করা হয়।

বডি পেলোড: একটি বইয়ের বস্তু। শুধুমাত্র যে ক্ষেত্রগুলির আপডেট প্রয়োজন সেগুলি পাস করা যেতে পারে, অন্যগুলি ঐচ্ছিক৷

ক্যোয়ারী প্যারামিটার: কোনোটিই নয়।

রিটার্নস: আপডেট করা বই।

স্ট্যাটাস কোড:

  • 200 — যখন বইটি সফলভাবে আপডেট করা হয়,
  • 400 - যদি একটি ত্রুটি ঘটে,
  • 406 — যদি isbn কোডটি অবৈধ হয়।

মুছুন /বই/{isbn}

একটি বিদ্যমান বই মুছে দেয়, যা এর isbn দ্বারা চিহ্নিত করা হয়েছে পাথ প্যারামিটার হিসাবে।

বডি পেলোড: কোনোটিই নয়।

ক্যোয়ারী প্যারামিটার: কোনোটিই নয়।

রিটার্ন: কিছুই না।

স্ট্যাটাস কোড:

  • 204 - যখন বইটি সফলভাবে মুছে ফেলা হয়,
  • 400 - যদি একটি ত্রুটি ঘটে।

8. একটি পাত্রে একটি REST API স্থাপন করুন এবং প্রকাশ করুন৷

কোড অন্বেষণ

ডকারফাইল

আসুন Dockerfile দেখে শুরু করি, যা আমাদের অ্যাপ্লিকেশন কোড কন্টেইনারাইজ করার জন্য দায়ী হবে:

FROM node:20-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
CMD [ "node", "index.js" ]

আমরা একটি Node.JS 20 "স্লিম" ইমেজ ব্যবহার করছি। আমরা /usr/src/app ডিরেক্টরিতে কাজ করছি। আমরা package.json ফাইলটি অনুলিপি করছি (নিচে বিস্তারিত) যা আমাদের নির্ভরতাকে সংজ্ঞায়িত করে, অন্যান্য জিনিসগুলির মধ্যে। আমরা সোর্স কোড কপি করে npm install দিয়ে নির্ভরতা ইনস্টল করি। সবশেষে, আমরা নির্দেশ করি কিভাবে এই অ্যাপ্লিকেশনটি চালানো হবে, node index.js কমান্ড দিয়ে।

package.json

এর পরে, আমরা package.json ফাইলটি দেখতে পারি:

{
    "name": "run-crud",
    "description": "CRUD operations over book data",
    "license": "Apache-2.0",
    "engines": {
        "node": ">= 20.0.0"
    },
    "dependencies": {
        "@google-cloud/firestore": "^4.9.9",
        "cors": "^2.8.5",
        "express": "^4.17.1",
        "isbn3": "^1.1.10"
    },
    "scripts": {
        "start": "node index.js"
    }
}

আমরা উল্লেখ করি যে আমরা Node.JS 14 ব্যবহার করতে চাই, যেমনটি Dockerfile ক্ষেত্রে ছিল।

আমাদের ওয়েব API অ্যাপ্লিকেশন নির্ভর করে:

  • ডাটাবেসে বইয়ের ডেটা অ্যাক্সেস করার জন্য Firestore NPM মডিউল,
  • CORS (ক্রস অরিজিন রিসোর্স শেয়ারিং) অনুরোধগুলি পরিচালনা করার জন্য cors লাইব্রেরি, কারণ আমাদের REST API আমাদের অ্যাপ ইঞ্জিন ওয়েব অ্যাপ্লিকেশন ফ্রন্টএন্ডের ক্লায়েন্ট কোড থেকে আহ্বান করা হবে,
  • এক্সপ্রেস ফ্রেমওয়ার্ক, যা আমাদের API ডিজাইন করার জন্য আমাদের ওয়েব ফ্রেমওয়ার্ক হবে,
  • এবং তারপর isbn3 মডিউল যা বইয়ের ISBN কোড যাচাই করতে সাহায্য করে।

আমরা start স্ক্রিপ্টও নির্দিষ্ট করি, যা স্থানীয়ভাবে অ্যাপ্লিকেশন শুরু করার জন্য, বিকাশ এবং পরীক্ষার উদ্দেশ্যে কাজে আসবে।

index.js

আসুন index.js এ গভীরভাবে নজর দিয়ে কোডের মাংসে যাওয়া যাক:

const Firestore = require('@google-cloud/firestore');
const firestore = new Firestore();
const bookStore = firestore.collection('books');

আমাদের প্রয়োজন Firestore মডিউল, এবং books সংগ্রহের রেফারেন্স, যেখানে আমাদের বইয়ের ডেটা সংরক্ষণ করা হয়।

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());

const querystring = require('querystring');

const cors = require('cors');
app.use(cors({
    exposedHeaders: ['Content-Length', 'Content-Type', 'Link'],
}));

আমরা আমাদের REST API বাস্তবায়নের জন্য আমাদের ওয়েব ফ্রেমওয়ার্ক হিসাবে এক্সপ্রেস ব্যবহার করছি। আমরা আমাদের API এর সাথে বিনিময় করা JSON পেলোড পার্স করতে body-parser মডিউল ব্যবহার করছি।

querystring মডিউল ইউআরএল ম্যানিপুলেট করতে সহায়ক। আমরা যখন পেজিনেশনের উদ্দেশ্যে Link শিরোনাম তৈরি করি তখন এটি হবে (পরে এই বিষয়ে আরও)।

তারপর আমরা cors মডিউল কনফিগার করি। আমরা যে শিরোনামগুলিকে CORS-এর মাধ্যমে পাস করতে চাই তা স্পষ্ট করে দিই, কারণ বেশিরভাগই সাধারণত ছিনিয়ে নেওয়া হয়, কিন্তু এখানে, আমরা সাধারণ বিষয়বস্তুর দৈর্ঘ্য এবং প্রকার রাখতে চাই, সেইসাথে Link শিরোলেখ যা আমরা পেজিনেশনের জন্য নির্দিষ্ট করব।

const ISBN = require('isbn3');

function isbnOK(isbn, res) {
    const parsedIsbn = ISBN.parse(isbn);
    if (!parsedIsbn) {
        res.status(406)
            .send({error: `Invalid ISBN: ${isbn}`});
        return false;
    }
    return parsedIsbn;
}

আমরা আইএসবিএন কোডগুলি পার্স এবং যাচাই করার জন্য isbn3 NPM মডিউল ব্যবহার করব, এবং আমরা একটি ছোট ইউটিলিটি ফাংশন বিকাশ করব যা ISBN কোডগুলিকে পার্স করবে এবং ISBN কোডগুলি অবৈধ হলে প্রতিক্রিয়াতে একটি 406 স্ট্যাটাস কোড দিয়ে প্রতিক্রিয়া জানাবে।

  • GET /books

আসুন GET /books এন্ডপয়েন্ট দেখে নেওয়া যাক, টুকরো টুকরো:

app.get('/books', async (req, res) => {
    try {
        var query = new Firestore().collection('books');

        if (!!req.query.author) {
            console.log(`Filtering by author: ${req.query.author}`);
            query = query.where("author", "==", req.query.author);
        }
        if (!!req.query.language) {
            console.log(`Filtering by language: ${req.query.language}`);
            query = query.where("language", "==", req.query.language);
        }

        const page = parseInt(req.query.page) || 0;

        // - -  - -  - -  - -  - -  - -

    } catch (e) {
        console.error('Failed to fetch books', e);
        res.status(400)
            .send({error: `Impossible to fetch books: ${e.message}`});
    }
});

আমরা একটি ক্যোয়ারী প্রস্তুত করে ডাটাবেস অনুসন্ধানের জন্য প্রস্তুত হচ্ছি। লেখক এবং/অথবা ভাষা দ্বারা ফিল্টার করার জন্য এই ক্যোয়ারীটি ঐচ্ছিক ক্যোয়ারী প্যারামিটারের উপর নির্ভর করবে। আমরা 10টি বইয়ের খণ্ডে বইয়ের তালিকাও ফিরিয়ে দিচ্ছি।

বইগুলি আনার সময় যদি পথে কোনও ত্রুটি থাকে, আমরা একটি 400 স্ট্যাটাস কোড সহ একটি ত্রুটি ফেরত দিই৷

আসুন সেই শেষ পয়েন্টের স্নিপ করা অংশে জুম করি:

        const snapshot = await query
            .orderBy('updated', 'desc')
            .limit(PAGE_SIZE)
            .offset(PAGE_SIZE * page)
            .get();

        const books = [];

        if (snapshot.empty) {
            console.log('No book found');
        } else {
            snapshot.forEach(doc => {
                const {title, author, pages, year, language, ...otherFields} = doc.data();
                const book = {isbn: doc.id, title, author, pages, year, language};
                books.push(book);
            });
        }

পূর্ববর্তী বিভাগে, আমরা author এবং language দ্বারা ফিল্টার করেছি, কিন্তু এই বিভাগে, আমরা শেষ আপডেট করা তারিখের ক্রম অনুসারে বইয়ের তালিকা বাছাই করতে যাচ্ছি (শেষ আপডেট প্রথমে আসে)। এবং আমরা একটি সীমা (প্রত্যাবর্তনের উপাদানগুলির সংখ্যা) এবং একটি অফসেট (যেখান থেকে পরবর্তী বইগুলির পরবর্তী ব্যাচটি ফেরত দিতে হবে সেই প্রারম্ভিক বিন্দু) সংজ্ঞায়িত করে ফলাফলটি পৃষ্ঠাবিন্যাস করব।

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

আসুন একটি ভাল অনুশীলন দেখে এই শেষ পয়েন্টের ব্যাখ্যাগুলি শেষ করি: ডেটার প্রথম, পূর্ববর্তী, পরবর্তী বা শেষ পৃষ্ঠাগুলিতে URI লিঙ্কগুলিকে সংজ্ঞায়িত করতে Link শিরোনাম ব্যবহার করুন (আমাদের ক্ষেত্রে, আমরা শুধুমাত্র পূর্ববর্তী এবং পরবর্তী প্রদান করব)।

        var links = {};
        if (page > 0) {
            const prevQuery = querystring.stringify({...req.query, page: page - 1});
            links.prev = `${req.path}${prevQuery != '' ? `?${prevQuery}` : ''}`;
        }
        if (snapshot.docs.length === PAGE_SIZE) {
            const nextQuery = querystring.stringify({...req.query, page: page + 1});
            links.next = `${req.path}${nextQuery != '' ? `?${nextQuery}` : ''}`;
        }
        if (Object.keys(links).length > 0) {
            res.links(links);
        }

        res.status(200).send(books);

যুক্তিটি এখানে প্রথমে কিছুটা জটিল মনে হতে পারে, তবে আমরা যা করছি তা হল পূর্ববর্তী লিঙ্ক যোগ করা যদি আমরা ডেটার প্রথম পৃষ্ঠায় না থাকি। এবং আমরা একটি পরবর্তী লিঙ্ক যোগ করি যদি ডেটার পৃষ্ঠাটি পূর্ণ থাকে (অর্থাৎ, PAGE_SIZE ধ্রুবক দ্বারা সংজ্ঞায়িত সর্বাধিক সংখ্যক বই রয়েছে, ধরে নিই যে আরও একটি আরও ডেটা নিয়ে আসছে)। তারপরে আমরা সঠিক সিনট্যাক্স সহ সঠিক শিরোনাম তৈরি করতে এক্সপ্রেসের resource#links() ফাংশন ব্যবহার করি।

আপনার তথ্যের জন্য, লিঙ্ক শিরোনামটি এরকম কিছু দেখাবে:

link: </books?page=1>; rel="prev", </books?page=3>; rel="next"
  • POST /books এবং POST /books/:isbn

উভয় শেষ পয়েন্ট একটি নতুন বই তৈরি করতে এখানে আছে. একটি বই পেলোডে ISBN কোড পাস করে, যেখানে অন্যটি এটিকে পাথ প্যারামিটার হিসাবে পাস করে। যেভাবেই হোক, উভয়ই আমাদের createBook() ফাংশনকে কল করে:

async function createBook(isbn, req, res) {
    const parsedIsbn = isbnOK(isbn, res);
    if (!parsedIsbn) return;

    const {title, author, pages, year, language} = req.body;

    try {
        const docRef = bookStore.doc(parsedIsbn.isbn13);
        await docRef.set({
            title, author, pages, year, language,
            updated: Firestore.Timestamp.now()
        });
        console.log(`Saved book ${parsedIsbn.isbn13}`);

        res.status(201)
            .location(`/books/${parsedIsbn.isbn13}`)
            .send({status: `Book ${parsedIsbn.isbn13} created`});
    } catch (e) {
        console.error(`Failed to save book ${parsedIsbn.isbn13}`, e);
        res.status(400)
            .send({error: `Impossible to create book ${parsedIsbn.isbn13}: ${e.message}`});
    }    
}

আমরা isbn কোডটি বৈধ কিনা তা পরীক্ষা করি, অন্যথায় ফাংশন থেকে ফিরে আসব (এবং একটি 406 স্ট্যাটাস কোড সেট করুন)। আমরা অনুরোধের অংশে পাস করা পেলোড থেকে বইয়ের ক্ষেত্রগুলি পুনরুদ্ধার করি। তারপর আমরা Firestore এ বইয়ের বিবরণ সংরক্ষণ করতে যাচ্ছি। সাফল্যের উপর 201 , এবং ব্যর্থতার জন্য 400 ফিরে আসছে।

সফলভাবে ফিরে আসার সময়, আমরা লোকেশন হেডারও সেট করি, যাতে নতুন তৈরি রিসোর্স যেখানে API-এর ক্লায়েন্টকে ইঙ্গিত দিতে পারে। হেডার নিচের মত দেখাবে:

Location: /books/9781234567898
  • GET /books/:isbn

ফায়ারস্টোর থেকে আইএসবিএন-এর মাধ্যমে চিহ্নিত একটি বই নিয়ে আসা যাক।

app.get('/books/:isbn', async (req, res) => {
    const parsedIsbn = isbnOK(req.params.isbn, res);
    if (!parsedIsbn) return;

    try {
        const docRef = bookStore.doc(parsedIsbn.isbn13);
        const docSnapshot = await docRef.get();

        if (!docSnapshot.exists) {
            console.log(`Book not found ${parsedIsbn.isbn13}`)
            res.status(404)
                .send({error: `Could not find book ${parsedIsbn.isbn13}`});
            return;
        }

        console.log(`Fetched book ${parsedIsbn.isbn13}`, docSnapshot.data());

        const {title, author, pages, year, language, ...otherFields} = docSnapshot.data();
        const book = {isbn: parsedIsbn.isbn13, title, author, pages, year, language};

        res.status(200).send(book);
    } catch (e) {
        console.error(`Failed to fetch book ${parsedIsbn.isbn13}`, e);
        res.status(400)
            .send({error: `Impossible to fetch book ${parsedIsbn.isbn13}: ${e.message}`});
    }
});

বরাবরের মতো, আমরা ISBN বৈধ কিনা তা পরীক্ষা করি। বইটি পুনরুদ্ধার করার জন্য আমরা ফায়ারস্টোরে একটি প্রশ্ন করি। snapshot.exists সম্পত্তিটি সত্যিই একটি বই পাওয়া গেছে কিনা তা জানার জন্য সহজ। অন্যথায়, আমরা একটি ত্রুটি এবং একটি 404 পাওয়া যায়নি স্ট্যাটাস কোড ফেরত পাঠাই। আমরা বইয়ের ডেটা পুনরুদ্ধার করি এবং ফেরত দেওয়ার জন্য বইটির প্রতিনিধিত্ব করে একটি JSON অবজেক্ট তৈরি করি।

  • PUT /books/:isbn

আমরা একটি বিদ্যমান বই আপডেট করতে PUT পদ্ধতি ব্যবহার করছি।

app.put('/books/:isbn', async (req, res) => {
    const parsedIsbn = isbnOK(req.params.isbn, res);
    if (!parsedIsbn) return;

    try {
        const docRef = bookStore.doc(parsedIsbn.isbn13);
        await docRef.set({
            ...req.body,
            updated: Firestore.Timestamp.now()
        }, {merge: true});
        console.log(`Updated book ${parsedIsbn.isbn13}`);

        res.status(201)
            .location(`/books/${parsedIsbn.isbn13}`)
            .send({status: `Book ${parsedIsbn.isbn13} updated`});
    } catch (e) {
        console.error(`Failed to update book ${parsedIsbn.isbn13}`, e);
        res.status(400)
            .send({error: `Impossible to update book ${parsedIsbn.isbn13}: ${e.message}`});
    }    
});

আমরা সর্বশেষ কখন সেই রেকর্ডটি আপডেট করেছি তা মনে রাখার জন্য আমরা updated তারিখ/সময় ক্ষেত্রটি আপডেট করি। আমরা {merge:true} কৌশল ব্যবহার করি যা বিদ্যমান ক্ষেত্রগুলিকে তাদের নতুন মান দিয়ে প্রতিস্থাপন করে (অন্যথায়, সমস্ত ক্ষেত্রগুলি সরানো হয়, এবং শুধুমাত্র পেলোডের নতুন ক্ষেত্রগুলি সংরক্ষণ করা হবে, পূর্ববর্তী আপডেট বা প্রাথমিক সৃষ্টি থেকে বিদ্যমান ক্ষেত্রগুলি মুছে ফেলা হবে)।

আমরা বইয়ের URI-এ নির্দেশ করার জন্য Location শিরোনামও সেট করি।

  • DELETE /books/:isbn

বই মুছে ফেলা বেশ সহজবোধ্য। ডকুমেন্ট রেফারেন্সে আমরা শুধু delete() পদ্ধতিকে কল করি। আমরা একটি 204 স্ট্যাটাস কোড ফেরত দিই, কারণ আমরা কোনো বিষয়বস্তু ফেরত দিচ্ছি না।

app.delete('/books/:isbn', async (req, res) => {
    const parsedIsbn = isbnOK(req.params.isbn, res);
    if (!parsedIsbn) return;

    try {
        const docRef = bookStore.doc(parsedIsbn.isbn13);
        await docRef.delete();
        console.log(`Book ${parsedIsbn.isbn13} was deleted`);

        res.status(204).end();
    } catch (e) {
        console.error(`Failed to delete book ${parsedIsbn.isbn13}`, e);
        res.status(400)
            .send({error: `Impossible to delete book ${parsedIsbn.isbn13}: ${e.message}`});
    }
});

এক্সপ্রেস / নোড সার্ভার শুরু করুন

শেষ কিন্তু অন্তত নয়, আমরা সার্ভার শুরু করি, ডিফল্টরূপে পোর্ট 8080 শুনছি:

const port = process.env.PORT || 8080;
app.listen(port, () => {
    console.log(`Books Web API service: listening on port ${port}`);
    console.log(`Node ${process.version}`);
});

স্থানীয়ভাবে অ্যাপ্লিকেশন চালানো

স্থানীয়ভাবে অ্যাপ্লিকেশন চালানোর জন্য, আমরা প্রথমে এর সাথে নির্ভরতাগুলি ইনস্টল করব:

$ npm install

এবং তারপরে আমরা শুরু করতে পারি:

$ npm start

সার্ভারটি localhost শুরু হবে এবং ডিফল্টরূপে পোর্ট 8080 এ শুনবে।

এটি একটি ডকার কন্টেইনার তৈরি করা এবং নিম্নলিখিত কমান্ডগুলির সাথে পাশাপাশি কন্টেইনার চিত্রটি চালানোও সম্ভব:

$ docker build -t crud-web-api .

$ docker run --rm -p 8080:8080 -it crud-web-api

ক্লাউড বিল্ডের সাহায্যে ক্লাউডে তৈরি করার সাথে সাথে আমাদের অ্যাপ্লিকেশনটির কন্টেইনারাইজেশন ভালভাবে চলবে কিনা তা পরীক্ষা করার জন্য ডকারের মধ্যে চালানোও একটি দুর্দান্ত উপায়।

API পরীক্ষা করা হচ্ছে

আমরা যেভাবে REST API কোড চালাই না কেন (সরাসরি নোডের মাধ্যমে বা ডকার কন্টেইনার ইমেজের মাধ্যমে), আমরা এখন এটির বিরুদ্ধে কয়েকটি প্রশ্ন চালাতে সক্ষম।

  • একটি নতুন বই তৈরি করুন (বডি পেলোডে আইএসবিএন):
$ curl -XPOST -d '{"isbn":"9782070368228","title":"Book","author":"me","pages":123,"year":2021,"language":"French"}' \
    -H "Content-Type: application/json" \
    http://localhost:8080/books
  • একটি নতুন বই তৈরি করুন (একটি পাথ প্যারামিটারে আইএসবিএন):
$ curl -XPOST -d '{"title":"Book","author":"me","pages":123,"year":2021,"language":"French"}' \
    -H "Content-Type: application/json" \
    http://localhost:8080/books/9782070368228
  • একটি বই মুছুন (যেটি আমরা তৈরি করেছি):
$ curl -XDELETE http://localhost:8080/books/9782070368228
  • ISBN দ্বারা একটি বই পুনরুদ্ধার করুন:
$ curl http://localhost:8080/books/9780140449136
$ curl http://localhost:8080/books/9782070360536
  • একটি বিদ্যমান বই এর শিরোনাম পরিবর্তন করে আপডেট করুন:
$ curl -XPUT \
       -d '{"title":"Book"}' \
       -H "Content-Type: application/json" \      
       http://localhost:8080/books/9780003701203
  • বইয়ের তালিকা পুনরুদ্ধার করুন (প্রথম 10):
$ curl http://localhost:8080/books
  • একটি নির্দিষ্ট লেখকের লেখা বই খুঁজুন:
$ curl http://localhost:8080/books?author=Virginia+Woolf
  • ইংরেজিতে লেখা বইগুলোর তালিকা করুন:
$ curl http://localhost:8080/books?language=English
  • বইয়ের 4র্থ পৃষ্ঠা লোড করুন:
$ curl http://localhost:8080/books?page=3

আমরা আমাদের অনুসন্ধানকে পরিমার্জিত করতে author , language এবং books ক্যোয়ারী প্যারামিটারগুলিকেও একত্রিত করতে পারি।

কনটেইনারাইজড REST API তৈরি এবং স্থাপন করা

যেহেতু আমরা খুশি যে REST API পরিকল্পনা অনুযায়ী কাজ করে, এটি ক্লাউডে, ক্লাউড রানে স্থাপন করার সঠিক মুহূর্ত!

আমরা এটি দুটি ধাপে করতে যাচ্ছি:

  • প্রথমে, নিম্নলিখিত কমান্ডের সাহায্যে ক্লাউড বিল্ড দিয়ে কন্টেইনার ইমেজ তৈরি করে:
$ gcloud builds submit \
         --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/crud-web-api
  • তারপরে, এই দ্বিতীয় কমান্ডের সাথে পরিষেবাটি স্থাপন করে:
$ gcloud run deploy run-crud \
         --image gcr.io/${GOOGLE_CLOUD_PROJECT}/crud-web-api \
         --allow-unauthenticated \
         --region=${REGION} \
         --platform=managed

প্রথম কমান্ডের সাথে, ক্লাউড বিল্ড কন্টেইনার ইমেজ তৈরি করে এবং কনটেইনার রেজিস্ট্রিতে এটি হোস্ট করে। পরবর্তী কমান্ড রেজিস্ট্রি থেকে কন্টেইনার ইমেজ স্থাপন করে, এবং এটি ক্লাউড অঞ্চলে স্থাপন করে।

আমরা ক্লাউড কনসোল UI এ দুবার চেক করতে পারি যে আমাদের ক্লাউড রান পরিষেবাটি এখন তালিকায় উপস্থিত রয়েছে:

f62fbca02a8127c0.png

একটি শেষ ধাপ যা আমরা এখানে করব, তা হল সদ্য স্থাপন করা ক্লাউড রান পরিষেবার URL পুনরুদ্ধার করা, নিম্নলিখিত কমান্ডের জন্য ধন্যবাদ:

$ export RUN_CRUD_SERVICE_URL=$(gcloud run services describe run-crud \
                                       --region=${REGION} \
                                       --platform=managed \
                                       --format='value(status.url)')

পরবর্তী বিভাগে আমাদের ক্লাউড রান REST API-এর URL প্রয়োজন হবে, কারণ আমাদের অ্যাপ ইঞ্জিন ফ্রন্টএন্ড কোড API-এর সাথে ইন্টারঅ্যাক্ট করবে।

9. লাইব্রেরি ব্রাউজ করতে একটি ওয়েব অ্যাপ হোস্ট করুন

এই প্রজেক্টে কিছু গ্লিটার যোগ করার জন্য ধাঁধার শেষ অংশটি হল একটি ওয়েব ফ্রন্টএন্ড প্রদান করা যা আমাদের REST API এর সাথে ইন্টারঅ্যাক্ট করবে। সেই উদ্দেশ্যে, আমরা কিছু ক্লায়েন্ট জাভাস্ক্রিপ্ট কোড সহ Google অ্যাপ ইঞ্জিন ব্যবহার করব যা AJAX অনুরোধের মাধ্যমে API কল করবে (ক্লায়েন্ট-সাইড ফেচ API ব্যবহার করে)।

আমাদের অ্যাপ্লিকেশন, যদিও Node.JS অ্যাপ ইঞ্জিন রানটাইমে স্থাপন করা হয়েছে, বেশিরভাগই স্ট্যাটিক রিসোর্স দিয়ে তৈরি! খুব বেশি ব্যাকএন্ড কোড নেই, কারণ বেশিরভাগ ব্যবহারকারীর মিথস্ক্রিয়া ক্লায়েন্ট-সাইড জাভাস্ক্রিপ্টের মাধ্যমে ব্রাউজারে হবে। আমরা কোনো অভিনব ফ্রন্টএন্ড জাভাস্ক্রিপ্ট ফ্রেমওয়ার্ক ব্যবহার করব না, আমরা শুধু কিছু "ভ্যানিলা" জাভাস্ক্রিপ্ট ব্যবহার করব, UI-এর জন্য কিছু ওয়েব কম্পোনেন্ট সহ Shoelace ওয়েব কম্পোনেন্ট লাইব্রেরি ব্যবহার করে:

  • বইয়ের ভাষা নির্বাচন করতে একটি নির্বাচন বাক্স:

6fb9f741000a2dc1.png

  • একটি নির্দিষ্ট বই সম্পর্কে বিশদ বিবরণ প্রদর্শনের জন্য একটি কার্ড উপাদান (বইটির ISBN প্রতিনিধিত্ব করার জন্য একটি বারকোড সহ, JsBarcode লাইব্রেরি ব্যবহার করে):

3aa21a9e16e3244e.png

  • এবং ডাটাবেস থেকে আরও বই লোড করার জন্য একটি বোতাম:

3925ad81c91bbac9.png

এই সমস্ত ভিজ্যুয়াল উপাদানগুলিকে একত্রিত করার সময়, আমাদের লাইব্রেরি ব্রাউজ করার জন্য ফলস্বরূপ ওয়েব পৃষ্ঠাটি নিম্নরূপ দেখাবে:

18a5117150977d6.png

app.yaml কনফিগারেশন ফাইল

এর app.yaml কনফিগারেশন ফাইলটি দেখে এই অ্যাপ ইঞ্জিন অ্যাপ্লিকেশনটির কোড বেসে ডাইভিং শুরু করা যাক। এটি একটি ফাইল যা অ্যাপ ইঞ্জিনের জন্য নির্দিষ্ট, এবং পরিবেশের ভেরিয়েবল, অ্যাপ্লিকেশনের বিভিন্ন "হ্যান্ডলার" বা কিছু সংস্থান স্ট্যাটিক সম্পদ যা অ্যাপ ইঞ্জিনের অন্তর্নির্মিত দ্বারা পরিবেশন করা হবে তা উল্লেখ করার মতো জিনিসগুলি কনফিগার করার অনুমতি দেয় সিডিএন।

runtime: nodejs14

env_variables:
  RUN_CRUD_SERVICE_URL: CHANGE_ME

handlers:

- url: /js
  static_dir: public/js

- url: /css
  static_dir: public/css

- url: /img
  static_dir: public/img

- url: /(.+\.html)
  static_files: public/html/\1
  upload: public/(.+\.html)

- url: /
  static_files: public/html/index.html
  upload: public/html/index\.html

- url: /.*
  secure: always
  script: auto

আমরা নির্দিষ্ট করি যে আমাদের অ্যাপ্লিকেশনটি একটি Node.JS একটি, এবং আমরা 14 সংস্করণ ব্যবহার করতে চাই৷

তারপরে আমরা একটি পরিবেশ পরিবর্তনশীল সংজ্ঞায়িত করি যা আমাদের ক্লাউড রান পরিষেবা URL-এ নির্দেশ করে। আমাদের সঠিক URL সহ CHANGE_ME স্থানধারক আপডেট করতে হবে (কীভাবে এটি পরিবর্তন করতে হয় তা নীচে দেখুন)।

এর পরে, আমরা বিভিন্ন হ্যান্ডলারকে সংজ্ঞায়িত করি। প্রথম ৩টি এইচটিএমএল, সিএসএস এবং জাভাস্ক্রিপ্ট ক্লায়েন্ট-সাইড কোড অবস্থানের দিকে নির্দেশ করছে, public/ ফোল্ডার এবং এর সাব-ফোল্ডারের নিচে। চতুর্থটি নির্দেশ করে যে আমাদের অ্যাপ ইঞ্জিন অ্যাপ্লিকেশনটির মূল URL index.html পৃষ্ঠায় নির্দেশ করা উচিত। এইভাবে, ওয়েবসাইটের রুট অ্যাক্সেস করার সময় আমরা URL-এ index.html প্রত্যয় দেখতে পাব না। এবং শেষটি হল ডিফল্ট একটি যা আমাদের Node.JS অ্যাপ্লিকেশনে (অর্থাৎ অ্যাপ্লিকেশনটির "গতিশীল" অংশ, আমাদের বর্ণনা করা স্ট্যাটিক সম্পদের বিপরীতে) অন্যান্য সমস্ত URL ( /.* ) রুট করবে৷

এখন ক্লাউড রান পরিষেবার ওয়েব API URL আপডেট করা যাক৷

appengine-frontend/ ডিরেক্টরিতে, আমাদের ক্লাউড রান-ভিত্তিক REST API-এর URL-এ নির্দেশিত পরিবেশ পরিবর্তনশীল আপডেট করতে নিম্নলিখিত কমান্ডটি চালান:

$ sed -i -e "s|CHANGE_ME|${RUN_CRUD_SERVICE_URL}|" app.yaml

অথবা সঠিক URL দিয়ে app.yamlCHANGE_ME স্ট্রিং ম্যানুয়ালি পরিবর্তন করুন:

env_variables:
    RUN_CRUD_SERVICE_URL: CHANGE_ME

Node.JS package.json ফাইল

{
    "name": "appengine-frontend",
    "description": "Web frontend",
    "license": "Apache-2.0",
    "main": "index.js",
    "engines": {
        "node": "^14.0.0"
    },
    "dependencies": {
        "express": "^4.17.1",
        "isbn3": "^1.1.10"
    },
    "devDependencies": {
        "nodemon": "^2.0.7"
    },
    "scripts": {
        "start": "node index.js",
        "dev": "nodemon --watch server --inspect index.js"
    }
}

আমরা আবার জোর দিচ্ছি যে আমরা Node.JS 14 ব্যবহার করে এই অ্যাপ্লিকেশনটি চালাতে চাই। বইয়ের ISBN কোড যাচাই করার জন্য আমরা এক্সপ্রেস ফ্রেমওয়ার্কের পাশাপাশি isbn3 NPM মডিউলের উপর নির্ভর করি।

উন্নয়ন নির্ভরতাগুলিতে, আমরা ফাইলের পরিবর্তনগুলি নিরীক্ষণ করতে nodemon মডিউল ব্যবহার করতে যাচ্ছি। যদিও আমরা npm start এর মাধ্যমে স্থানীয়ভাবে আমাদের অ্যাপ্লিকেশন চালাতে পারি, কোডে কিছু পরিবর্তন করতে পারি, ^C দিয়ে অ্যাপটি বন্ধ করতে পারি এবং তারপরে এটি পুনরায় চালু করতে পারি, এটি কিছুটা ক্লান্তিকর। পরিবর্তে আমরা পরিবর্তনের পরে অ্যাপ্লিকেশনটি স্বয়ংক্রিয়ভাবে পুনরায় লোড / পুনরায় চালু করতে নিম্নলিখিত কমান্ডটি ব্যবহার করতে পারি:

$ npm run dev

index.js Node.JS কোড

const express = require('express');
const app = express();

app.use(express.static('public'));

const bodyParser = require('body-parser');
app.use(bodyParser.json());

আমরা এক্সপ্রেস ওয়েব ফ্রেমওয়ার্ক প্রয়োজন. আমরা নির্দিষ্ট করি যে সর্বজনীন ডিরেক্টরিতে স্ট্যাটিক সম্পদ রয়েছে যা static মিডলওয়্যার দ্বারা পরিবেশন করা যেতে পারে (অন্তত যখন ডেভেলপমেন্ট মোডে স্থানীয়ভাবে চলছে)। সবশেষে, আমাদের JSON পেলোড পার্স করার জন্য আমাদের body-parser প্রয়োজন।

আসুন আমরা সংজ্ঞায়িত করা রুটগুলির কয়েকটি দেখে নেওয়া যাক:

app.get('/', async (req, res) => {
    res.redirect('/html/index.html');
});

app.get('/webapi', async (req, res) => {
    res.send(process.env.RUN_CRUD_SERVICE_URL);
});

প্রথমটি মেলে / আমাদের public/html ডিরেক্টরিতে index.html এ পুনঃনির্দেশিত হবে। ডেভেলপমেন্ট মোডে যেমন আমরা অ্যাপ ইঞ্জিন রানটাইমের মধ্যে চলছি না, আমরা অ্যাপ ইঞ্জিনের ইউআরএল রাউটিং পাচ্ছি না। তাই পরিবর্তে, এখানে, আমরা কেবল রুট ইউআরএলকে HTML ফাইলে পুনঃনির্দেশ করছি।

দ্বিতীয় এন্ডপয়েন্ট যেটি আমরা সংজ্ঞায়িত করি /webapi আমাদের Cloud RUN REST API এর URL প্রদান করবে। এইভাবে, ক্লায়েন্ট-সাইড জাভাস্ক্রিপ্ট কোড জানতে পারবে বইয়ের তালিকা পেতে কোথায় কল করতে হবে।

const port = process.env.PORT || 8080;
app.listen(port, () => {
    console.log(`Book library web frontend: listening on port ${port}`);
    console.log(`Node ${process.version}`);
    console.log(`Web API endpoint ${process.env.RUN_CRUD_SERVICE_URL}`);
});

শেষ করার জন্য, আমরা এক্সপ্রেস ওয়েব অ্যাপ চালাচ্ছি এবং ডিফল্টরূপে পোর্ট 8080 এ শুনছি।

index.html পৃষ্ঠা

আমরা এই দীর্ঘ HTML পৃষ্ঠার প্রতিটি লাইন দেখব না। পরিবর্তে, এর কিছু মূল লাইন হাইলাইট করা যাক.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.37/dist/themes/base.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.37/dist/shoelace.js"></script>

<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.0/dist/barcodes/JsBarcode.ean-upc.min.js"></script>

<script src="/js/app.js"></script>
<link rel="stylesheet" type="text/css" href="/css/style.css">

প্রথম দুটি লাইন শোলেস ওয়েব কম্পোনেন্ট লাইব্রেরি (একটি স্ক্রিপ্ট এবং একটি স্টাইলশীট) আমদানি করে।

পরবর্তী লাইনটি JsBarcode লাইব্রেরি আমদানি করে, বইয়ের ISBN কোডের বারকোড তৈরি করতে।

শেষ লাইনগুলি আমাদের নিজস্ব জাভাস্ক্রিপ্ট কোড এবং CSS স্টাইলশীট আমদানি করছে, যা আমাদের public/ সাবডিরেক্টরিতে অবস্থিত।

HTML পৃষ্ঠার body , আমরা তাদের কাস্টম উপাদান ট্যাগের সাথে শোলেস উপাদান ব্যবহার করি, যেমন:

<sl-icon name="book-half"></sl-icon>
...

<sl-select id="language-select" placeholder="Select a language..." clearable>
    <sl-menu-item value="English">English</sl-menu-item>
    <sl-menu-item value="French">French</sl-menu-item>
    ...
</sl-select>
...

<sl-button id="more-button" type="primary" size="large">
    More books...
</sl-button>
...

এবং আমরা একটি বইয়ের প্রতিনিধিত্ব করতে HTML টেমপ্লেট এবং তাদের স্লট পূরণ করার ক্ষমতাও ব্যবহার করি। বইয়ের তালিকা তৈরি করতে আমরা সেই টেমপ্লেটের কপি তৈরি করব এবং বইগুলির বিশদ বিবরণ দিয়ে স্লটে মানগুলি প্রতিস্থাপন করব:

    <template id="book-card">
        <sl-card class="card-overview">
        ...
            <slot name="author">Author</slot>
            ... 
        </sl-card>
    </template>

যথেষ্ট এইচটিএমএল, আমরা কোড পর্যালোচনা প্রায় সম্পন্ন করেছি। একটি শেষ মাংসল অংশ বাকি: app.js ক্লায়েন্ট-সাইড জাভাস্ক্রিপ্ট কোড যা আমাদের REST API-এর সাথে ইন্টারঅ্যাক্ট করে।

app.js ক্লায়েন্ট-সাইড জাভাস্ক্রিপ্ট কোড

আমরা একটি শীর্ষ-স্তরের ইভেন্ট শ্রোতা দিয়ে শুরু করি যে DOM সামগ্রী লোড হওয়ার জন্য অপেক্ষা করে:

document.addEventListener("DOMContentLoaded", async function(event) {
    ...
}

এটি প্রস্তুত হয়ে গেলে, আমরা কিছু মূল ধ্রুবক এবং ভেরিয়েবল সেট আপ করতে পারি:

    const serverUrlResponse = await fetch('/webapi');
    const serverUrl = await serverUrlResponse.text();
    console.log('Web API endpoint:', serverUrl);
    
    const server = serverUrl + '/books';
    var page = 0;
    var language = '';

প্রথমত, আমরা আমাদের REST API-এর URL আনব, আমাদের অ্যাপ ইঞ্জিন নোড কোডের জন্য ধন্যবাদ যা আমরা app.yaml এ প্রাথমিকভাবে সেট করা এনভায়রনমেন্ট ভেরিয়েবল ফিরিয়ে দেয়। এনভায়রনমেন্ট ভেরিয়েবলের জন্য ধন্যবাদ, /webapi এন্ডপয়েন্ট, যা জাভাস্ক্রিপ্ট ক্লায়েন্ট-সাইড কোড থেকে কল করা হয়েছে, আমাদের ফ্রন্টএন্ড কোডে REST API URL হার্ডকোড করতে হয়নি।

আমরা একটি page এবং language ভেরিয়েবলও সংজ্ঞায়িত করি, যেগুলি আমরা পৃষ্ঠা সংখ্যা এবং ভাষা ফিল্টারিংয়ের ট্র্যাক রাখতে ব্যবহার করব।

    const moreButton = document.getElementById('more-button');
    moreButton.addEventListener('sl-focus', event => {
        console.log('Button clicked');
        moreButton.blur();

        appendMoreBooks(server, page++, language);
    });

বই লোড করার জন্য আমরা বোতামে একটি ইভেন্ট হ্যান্ডলার যোগ করি। এটি ক্লিক করা হলে, এটি appendMoreBooks() ফাংশন কল করতে যাচ্ছে।

    const langSelect = document.getElementById('language-select');
    langSelect.addEventListener('sl-change', event => {
        page = 0;
        language = event.srcElement.value;
        document.getElementById('library').replaceChildren();
        console.log(`Language selected: "${language}"`);

        appendMoreBooks(server, page++, language);
    });

নির্বাচন বাক্সের জন্য অনুরূপ জিনিস, আমরা ভাষা নির্বাচনের পরিবর্তন সম্পর্কে অবহিত করার জন্য একটি ইভেন্ট হ্যান্ডলার যোগ করি। এবং বোতামের মতো, আমরা REST API URL, বর্তমান পৃষ্ঠা এবং ভাষা নির্বাচন পাস করে appendMoreBooks() ফাংশনকে কল করি।

তাহলে আসুন সেই ফাংশনটি দেখে নেওয়া যাক যা বই নিয়ে আসে এবং যুক্ত করে:

async function appendMoreBooks(server, page, language) {
    const searchUrl = new URL(server);
    if (!!page) searchUrl.searchParams.append('page', page);
    if (!!language) searchUrl.searchParams.append('language', language);
        
    const response = await fetch(searchUrl.href);
    const books = await response.json();
    ... 
}

উপরে, আমরা REST API কল করার জন্য ব্যবহার করার জন্য সঠিক URL তৈরি করছি। তিনটি ক্যোয়ারী প্যারামিটার আছে যা আমরা সাধারণত নির্দিষ্ট করতে পারি, কিন্তু এখানে এই UI এ, আমরা শুধুমাত্র দুটি নির্দিষ্ট করি:

  • page - একটি পূর্ণসংখ্যা যা বইগুলির পৃষ্ঠা সংখ্যার জন্য বর্তমান পৃষ্ঠা নির্দেশ করে,
  • language - লিখিত ভাষা দ্বারা ফিল্টার করার জন্য একটি ভাষা স্ট্রিং।

তারপরে আমরা আমাদের বইয়ের বিবরণ সহ JSON অ্যারে পুনরুদ্ধার করতে Fetch API ব্যবহার করি।

    const linkHeader = response.headers.get('Link')
    console.log('Link', linkHeader);
    if (!!linkHeader && linkHeader.indexOf('rel="next"') > -1) {
        console.log('Show more button');
        document.getElementById('buttons').style.display = 'block';
    } else {
        console.log('Hide more button');
        document.getElementById('buttons').style.display = 'none';
    }

Link শিরোনামটি প্রতিক্রিয়াতে উপস্থিত আছে কিনা তার উপর নির্ভর করে, আমরা [More books...] বোতামটি দেখাব বা লুকিয়ে রাখব, কারণ Link শিরোলেখটি একটি ইঙ্গিত যা আমাদের বলে যে আরও বই লোড করা বাকি আছে কিনা ( next একটি থাকবে Link হেডারে URL)।

    const library = document.getElementById('library');
    const template = document.getElementById('book-card');
    for (let book of books) {
        const bookCard = template.content.cloneNode(true);

        bookCard.querySelector('slot[name=title]').innerText = book.title;
        bookCard.querySelector('slot[name=language]').innerText = book.language;
        bookCard.querySelector('slot[name=author]').innerText = book.author;
        bookCard.querySelector('slot[name=year]').innerText = book.year;
        bookCard.querySelector('slot[name=pages]').innerText = book.pages;
        
        const img = document.createElement('img');
        img.setAttribute('id', book.isbn);
        img.setAttribute('class', 'img-barcode-' + book.isbn)
        bookCard.querySelector('slot[name=barcode]').appendChild(img);

        library.appendChild(bookCard);
        ... 
    }
}

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

JsBarcode('.img-barcode-' + book.isbn).EAN13(book.isbn, {fontSize: 18, textMargin: 0, height: 60}).render();

ISBN কোডকে একটু সুন্দর করতে, আমরা JsBarcode লাইব্রেরি ব্যবহার করে বাস্তব বইয়ের পিছনের কভারের মতো একটি সুন্দর বারকোড তৈরি করি!

স্থানীয়ভাবে অ্যাপ্লিকেশনটি চালানো এবং পরীক্ষা করা

এখনকার জন্য যথেষ্ট কোড, এটি অ্যাকশনে অ্যাপ্লিকেশন দেখার সময়। প্রথমত, আমরা বাস্তবে মোতায়েন করার আগে, ক্লাউড শেলের মধ্যে স্থানীয়ভাবে তা করব।

আমরা আমাদের অ্যাপ্লিকেশনের জন্য প্রয়োজনীয় NPM মডিউলগুলি এর সাথে ইনস্টল করি:

$ npm install

এবং আমরা হয় স্বাভাবিকের সাথে অ্যাপটি চালাই:

$ npm start

অথবা পরিবর্তনের স্বয়ংক্রিয়-রিলোডিং সহ nodemon ধন্যবাদ, এর সাথে:

$ npm run dev

অ্যাপ্লিকেশনটি স্থানীয়ভাবে চলছে, এবং আমরা http://localhost:8080 এ ব্রাউজার থেকে এটি অ্যাক্সেস করতে পারি।

অ্যাপ ইঞ্জিন অ্যাপ্লিকেশন স্থাপন করা হচ্ছে

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

অ্যাপ্লিকেশনটি স্থাপন করার জন্য, আসুন নিম্নলিখিত কমান্ডটি চালু করি:

$ gcloud app deploy -q

প্রায় এক মিনিট পরে, অ্যাপ্লিকেশন স্থাপন করা উচিত।

অ্যাপ্লিকেশনটি আকৃতির একটি URL-এ উপলব্ধ হবে: https://${GOOGLE_CLOUD_PROJECT}.appspot.com

আমাদের অ্যাপ ইঞ্জিন ওয়েব অ্যাপ্লিকেশনের UI অন্বেষণ করা হচ্ছে

এখন আপনি করতে পারেন:

  • আরো বই লোড করতে [More books...] বোতামে ক্লিক করুন।
  • শুধুমাত্র সেই ভাষায় বই দেখতে একটি নির্দিষ্ট ভাষা নির্বাচন করুন।
  • আপনি সমস্ত বইয়ের তালিকায় ফিরে আসতে, নির্বাচন বাক্সে ছোট ক্রস দিয়ে নির্বাচনটি পরিষ্কার করতে পারেন।

10. পরিষ্কার করুন (ঐচ্ছিক)

আপনি যদি অ্যাপটি রাখতে না চান, তাহলে আপনি খরচ বাঁচাতে এবং পুরো প্রকল্পটি মুছে দিয়ে সামগ্রিকভাবে ভালো ক্লাউড নাগরিক হতে সম্পদ পরিষ্কার করতে পারেন:

gcloud projects delete ${GOOGLE_CLOUD_PROJECT} 

11. অভিনন্দন!

আমরা ক্লাউড ফাংশন, অ্যাপ ইঞ্জিন এবং ক্লাউড রানকে ধন্যবাদ, বিভিন্ন ওয়েব এপিআই এন্ডপয়েন্ট এবং ওয়েব ফ্রন্টএন্ড প্রকাশ করতে, বইয়ের একটি লাইব্রেরি সঞ্চয়, আপডেট এবং ব্রাউজ করতে, REST API বিকাশের জন্য কিছু ভাল ডিজাইনের প্যাটার্ন অনুসরণ করে একটি সেট তৈরি করেছি। পথ

আমরা কভার করেছি কি

  • ক্লাউড ফাংশন
  • ক্লাউড ফায়ারস্টোর
  • ক্লাউড রান
  • অ্যাপ ইঞ্জিন

আরও এগিয়ে যাচ্ছে

আপনি যদি এই কংক্রিট উদাহরণটি আরও অন্বেষণ করতে চান এবং এটিকে প্রসারিত করতে চান তবে এখানে এমন জিনিসগুলির একটি তালিকা রয়েছে যা আপনি তদন্ত করতে চান:

  • ডেটা ইম্পোর্ট ফাংশন এবং REST API কন্টেইনারে একটি সাধারণ API ফ্যাসাড প্রদান করতে API গেটওয়ের সুবিধা নিন, API অ্যাক্সেস করার জন্য API কীগুলি পরিচালনা করার মতো বৈশিষ্ট্যগুলি যোগ করতে বা API গ্রাহকদের জন্য হারের সীমাবদ্ধতা সংজ্ঞায়িত করুন৷
  • নথিভুক্ত করতে এবং REST API-এর জন্য একটি পরীক্ষামূলক খেলার মাঠ অফার করতে অ্যাপ ইঞ্জিন অ্যাপ্লিকেশনে Swagger-UI নোড মডিউল স্থাপন করুন।
  • ফ্রন্টএন্ডে, বিদ্যমান ব্রাউজিং ক্ষমতার বাইরে, ডেটা সম্পাদনা করতে, নতুন বই এন্ট্রি তৈরি করতে অতিরিক্ত স্ক্রিন যুক্ত করুন। এছাড়াও, যেহেতু আমরা ক্লাউড ফায়ারস্টোর ডাটাবেস ব্যবহার করছি, পরিবর্তনের সাথে সাথে প্রদর্শিত বইয়ের ডেটা আপডেট করতে এর রিয়েল-টাইম বৈশিষ্ট্যটি ব্যবহার করুন।