এই কোডল্যাব সম্পর্কে
1. ওভারভিউ
ক্লাউড রান হল একটি সম্পূর্ণরূপে পরিচালিত প্ল্যাটফর্ম যা আপনাকে সরাসরি Google-এর মাপযোগ্য পরিকাঠামোর উপরে আপনার কোড চালাতে সক্ষম করে। এই কোডল্যাব প্রদর্শন করবে কিভাবে Node.js অ্যাডমিন SDK ব্যবহার করে ক্লাউড রানে একটি ফায়ারস্টোর ডাটাবেসের সাথে একটি কৌণিক অ্যাপ্লিকেশন সংযোগ করতে হয়।
এই ল্যাবে, আপনি শিখবেন কিভাবে:
- একটি ফায়ারস্টোর ডাটাবেস তৈরি করুন
- ক্লাউড রানে একটি অ্যাপ্লিকেশন স্থাপন করুন যা আপনার ফায়ারস্টোর ডাটাবেসের সাথে সংযোগ করে
2. পূর্বশর্ত
- আপনার যদি ইতিমধ্যে একটি Google অ্যাকাউন্ট না থাকে তবে আপনাকে অবশ্যই একটি Google অ্যাকাউন্ট তৈরি করতে হবে।
- কাজের বা স্কুল অ্যাকাউন্টের পরিবর্তে একটি ব্যক্তিগত অ্যাকাউন্ট ব্যবহার করুন। কর্মক্ষেত্র এবং স্কুল অ্যাকাউন্টগুলিতে বিধিনিষেধ থাকতে পারে যা আপনাকে এই ল্যাবের জন্য প্রয়োজনীয় APIগুলি সক্ষম করতে বাধা দেয়৷
3. প্রকল্প সেটআপ
- Google ক্লাউড কনসোলে সাইন-ইন করুন।
- ক্লাউড কনসোলে বিলিং সক্ষম করুন ।
- এই ল্যাবটি সম্পূর্ণ করতে ক্লাউড সংস্থানগুলিতে $1 USD-এর কম খরচ হওয়া উচিত৷
- আপনি আরও চার্জ এড়াতে সংস্থানগুলি মুছতে এই ল্যাবের শেষে পদক্ষেপগুলি অনুসরণ করতে পারেন৷
- নতুন ব্যবহারকারীরা $300 USD ফ্রি ট্রায়ালের জন্য যোগ্য৷
- একটি নতুন প্রকল্প তৈরি করুন বা একটি বিদ্যমান প্রকল্প পুনরায় ব্যবহার করতে বেছে নিন।
4. ক্লাউড শেল এডিটর খুলুন
- ক্লাউড শেল এডিটরে নেভিগেট করুন
- যদি টার্মিনালটি স্ক্রিনের নীচে প্রদর্শিত না হয় তবে এটি খুলুন:
- হ্যামবার্গার মেনুতে ক্লিক করুন
- টার্মিনাল ক্লিক করুন
- নতুন টার্মিনালে ক্লিক করুন
- হ্যামবার্গার মেনুতে ক্লিক করুন
- টার্মিনালে, এই কমান্ড দিয়ে আপনার প্রকল্প সেট করুন:
- বিন্যাস:
gcloud config set project [PROJECT_ID]
- উদাহরণ:
gcloud config set project lab-project-id-example
- আপনি যদি আপনার প্রকল্প আইডি মনে করতে না পারেন:
- আপনি আপনার সমস্ত প্রকল্প আইডি এর সাথে তালিকাভুক্ত করতে পারেন:
gcloud projects list | awk '/PROJECT_ID/{print $2}'
- আপনি আপনার সমস্ত প্রকল্প আইডি এর সাথে তালিকাভুক্ত করতে পারেন:
- বিন্যাস:
- অনুমোদন করার জন্য অনুরোধ করা হলে, চালিয়ে যেতে অনুমোদন ক্লিক করুন।
- আপনার এই বার্তাটি দেখতে হবে:
যদি আপনি একটিUpdated property [core/project].
WARNING
দেখতে পান এবং জিজ্ঞাসা করা হয়Do you want to continue (Y/N)?
, তাহলে আপনি সম্ভবত প্রজেক্ট আইডি ভুলভাবে প্রবেশ করেছেন।N
টিপুন,Enter
টিপুন এবং আবারgcloud config set project
কমান্ড চালানোর চেষ্টা করুন।
5. এপিআই সক্ষম করুন
টার্মিনালে, APIs সক্ষম করুন:
gcloud services enable \
firestore.googleapis.com \
run.googleapis.com \
artifactregistry.googleapis.com \
cloudbuild.googleapis.com
অনুমোদন করার জন্য অনুরোধ করা হলে, চালিয়ে যেতে অনুমোদন ক্লিক করুন।
এই কমান্ডটি সম্পূর্ণ হতে কয়েক মিনিট সময় নিতে পারে, তবে এটি শেষ পর্যন্ত এটির মতো একটি সফল বার্তা তৈরি করবে:
Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.
6. ফায়ারস্টোর ডেটাবেস তৈরি করুন
- ফায়ারস্টোর
gcloud firestore databases create
কমান্ড চালানgcloud firestore databases create --location=nam5
7. আবেদন প্রস্তুত করুন
একটি Next.js অ্যাপ্লিকেশন প্রস্তুত করুন যা HTTP অনুরোধে সাড়া দেয়।
-
task-app
নামে একটি নতুন Next.js প্রকল্প তৈরি করতে, কমান্ডটি ব্যবহার করুন:npx --yes @angular/cli@19.2.5 new task-app \
--minimal \
--inline-template \
--inline-style \
--ssr \
--server-routing \
--defaults task-app
ডিরেক্টরি পরিবর্তন করুন:cd task-app
- Firestore ডাটাবেসের সাথে ইন্টারঅ্যাক্ট করতে
firebase-admin
ইনস্টল করুন।npm install firebase-admin
- ক্লাউড শেল এডিটরে
server.ts
ফাইলটি খুলুন: একটি ফাইল এখন পর্দার উপরের অংশে উপস্থিত হওয়া উচিত। এখানে আপনি এইcloudshell edit src/server.ts
server.ts
ফাইলটি সম্পাদনা করতে পারেন। -
server.ts
ফাইলের বিদ্যমান বিষয়বস্তু মুছুন। - নিম্নলিখিত কোডটি অনুলিপি করুন এবং খোলা
server.ts
ফাইলে পেস্ট করুন:import {
AngularNodeAppEngine,
createNodeRequestHandler,
isMainModule,
writeResponseToNodeResponse,
} from '@angular/ssr/node';
import express from 'express';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { initializeApp, applicationDefault, getApps } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
type Task = {
id: string;
title: string;
status: 'IN_PROGRESS' | 'COMPLETE';
createdAt: number;
};
const credential = applicationDefault();
// Only initialize app if it does not already exist
if (getApps().length === 0) {
initializeApp({ credential });
}
const db = getFirestore();
const tasksRef = db.collection('tasks');
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const app = express();
const angularApp = new AngularNodeAppEngine();
app.use(express.json());
app.get('/api/tasks', async (req, res) => {
const snapshot = await tasksRef.orderBy('createdAt', 'desc').limit(100).get();
const tasks: Task[] = snapshot.docs.map(doc => ({
id: doc.id,
title: doc.data()['title'],
status: doc.data()['status'],
createdAt: doc.data()['createdAt'],
}));
res.send(tasks);
});
app.post('/api/tasks', async (req, res) => {
const newTaskTitle = req.body.title;
if(!newTaskTitle){
res.status(400).send("Title is required");
return;
}
await tasksRef.doc().create({
title: newTaskTitle,
status: 'IN_PROGRESS',
createdAt: Date.now(),
});
res.sendStatus(200);
});
app.put('/api/tasks', async (req, res) => {
const task: Task = req.body;
if (!task || !task.id || !task.title || !task.status) {
res.status(400).send("Invalid task data");
return;
}
await tasksRef.doc(task.id).set(task);
res.sendStatus(200);
});
app.delete('/api/tasks', async (req, res) => {
const task: Task = req.body;
if(!task || !task.id){
res.status(400).send("Task ID is required");
return;
}
await tasksRef.doc(task.id).delete();
res.sendStatus(200);
});
/**
* Serve static files from /browser
*/
app.use(
express.static(browserDistFolder, {
maxAge: '1y',
index: false,
redirect: false,
}),
);
/**
* Handle all other requests by rendering the Angular application.
*/
app.use('/**', (req, res, next) => {
angularApp
.handle(req)
.then((response) =>
response ? writeResponseToNodeResponse(response, res) : next(),
)
.catch(next);
});
/**
* Start the server if this module is the main entry point.
* The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
*/
if (isMainModule(import.meta.url)) {
const port = process.env['PORT'] || 4000;
app.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
/**
* Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions.
*/
export const reqHandler = createNodeRequestHandler(app); - ক্লাউড শেল এডিটরে
angular.json
ফাইলটি খুলুন: আমরা এখনcloudshell edit angular.json
angular.json
ফাইলে"externalDependencies": ["firebase-admin"]
লাইন যোগ করব। -
angular.json
ফাইলের বিদ্যমান বিষয়বস্তু মুছুন। - নিচের কোডটি কপি করুন এবং খোলা
angular.json
ফাইলে পেস্ট করুন:{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"task-app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"inlineTemplate": true,
"inlineStyle": true,
"skipTests": true
},
"@schematics/angular:class": {
"skipTests": true
},
"@schematics/angular:directive": {
"skipTests": true
},
"@schematics/angular:guard": {
"skipTests": true
},
"@schematics/angular:interceptor": {
"skipTests": true
},
"@schematics/angular:pipe": {
"skipTests": true
},
"@schematics/angular:resolver": {
"skipTests": true
},
"@schematics/angular:service": {
"skipTests": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/task-app",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
],
"scripts": [],
"server": "src/main.server.ts",
"outputMode": "server",
"ssr": {
"entry": "src/server.ts"
},
"externalDependencies": ["firebase-admin"]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "task-app:build:production"
},
"development": {
"buildTarget": "task-app:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"
}
}
}
}
}
"বাহ্যিক নির্ভরতা": ["ফায়ারবেস-অ্যাডমিন"]
- ক্লাউড শেল এডিটরে
app.component.ts
ফাইলটি খুলুন: একটি বিদ্যমান ফাইল এখন পর্দার উপরের অংশে উপস্থিত হওয়া উচিত। এখানে আপনি এইcloudshell edit src/app/app.component.ts
app.component.ts
ফাইলটি সম্পাদনা করতে পারেন। -
app.component.ts
ফাইলের বিদ্যমান বিষয়বস্তু মুছুন। - নিম্নলিখিত কোডটি অনুলিপি করুন এবং খোলা
app.component.ts
ফাইলে পেস্ট করুন:import { afterNextRender, Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
type Task = {
id: string;
title: string;
status: 'IN_PROGRESS' | 'COMPLETE';
createdAt: number;
};
@Component({
selector: 'app-root',
standalone: true,
imports: [FormsModule],
template: `
<section>
<input
type="text"
placeholder="New Task Title"
[(ngModel)]="newTaskTitle"
class="text-black border-2 p-2 m-2 rounded"
/>
<button (click)="addTask()">Add new task</button>
<table>
<tbody>
@for (task of tasks(); track task) {
@let isComplete = task.status === 'COMPLETE';
<tr>
<td>
<input
(click)="updateTask(task, { status: isComplete ? 'IN_PROGRESS' : 'COMPLETE' })"
type="checkbox"
[checked]="isComplete"
/>
</td>
<td>{{ task.title }}</td>
<td>{{ task.status }}</td>
<td>
<button (click)="deleteTask(task)">Delete</button>
</td>
</tr>
}
</tbody>
</table>
</section>
`,
styles: '',
})
export class AppComponent {
newTaskTitle = '';
tasks = signal<Task[]>([]);
constructor() {
afterNextRender({
earlyRead: () => this.getTasks()
});
}
async getTasks() {
const response = await fetch(`/api/tasks`);
const tasks = await response.json();
this.tasks.set(tasks);
}
async addTask() {
await fetch(`/api/tasks`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: this.newTaskTitle,
status: 'IN_PROGRESS',
createdAt: Date.now(),
}),
});
this.newTaskTitle = '';
await this.getTasks();
}
async updateTask(task: Task, newTaskValues: Partial<Task>) {
await fetch(`/api/tasks`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...task, ...newTaskValues }),
});
await this.getTasks();
}
async deleteTask(task: any) {
await fetch('/api/tasks', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(task),
});
await this.getTasks();
}
}
অ্যাপ্লিকেশন এখন স্থাপন করার জন্য প্রস্তুত.
8. ক্লাউড রানে অ্যাপ্লিকেশনটি স্থাপন করুন
- ক্লাউড রানে আপনার অ্যাপ্লিকেশন স্থাপন করতে নীচের কমান্ডটি চালান:
gcloud run deploy helloworld \
--region=us-central1 \
--source=. - অনুরোধ করা হলে, আপনি চালিয়ে যেতে চান তা নিশ্চিত করতে
Y
এবংEnter
টিপুন:Do you want to continue (Y/n)? Y
কয়েক মিনিট পরে, অ্যাপ্লিকেশনটি আপনাকে দেখার জন্য একটি URL প্রদান করবে।
আপনার অ্যাপ্লিকেশানটি কার্যকর দেখতে URL এ নেভিগেট করুন৷ যতবার আপনি URL পরিদর্শন করবেন বা পৃষ্ঠা রিফ্রেশ করবেন, আপনি টাস্ক অ্যাপটি দেখতে পাবেন।
9. অভিনন্দন
এই ল্যাবে, আপনি নিম্নলিখিতগুলি কীভাবে করতে হয় তা শিখেছেন:
- PostgreSQL উদাহরণের জন্য একটি ক্লাউড SQL তৈরি করুন
- ক্লাউড রানে একটি অ্যাপ্লিকেশন স্থাপন করুন যা আপনার ক্লাউড SQL ডাটাবেসের সাথে সংযোগ করে
পরিষ্কার করুন
ক্লাউড এসকিউএল-এর একটি বিনামূল্যের স্তর নেই এবং আপনি এটি ব্যবহার করা চালিয়ে গেলে আপনাকে চার্জ করবে। অতিরিক্ত চার্জ এড়াতে আপনি আপনার ক্লাউড প্রকল্প মুছে ফেলতে পারেন।
পরিষেবাটি ব্যবহার না হলে ক্লাউড রান চার্জ না করলেও, আর্টিফ্যাক্ট রেজিস্ট্রিতে কন্টেইনার ইমেজ সংরক্ষণ করার জন্য আপনাকে চার্জ করা হতে পারে। আপনার ক্লাউড প্রকল্প মুছে ফেলা সেই প্রকল্পের মধ্যে ব্যবহৃত সমস্ত সংস্থানের জন্য বিলিং বন্ধ করে দেয়।
আপনি যদি চান, প্রকল্প মুছে ফেলুন:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
আপনি আপনার ক্লাউডশেল ডিস্ক থেকে অপ্রয়োজনীয় সংস্থানগুলি মুছতেও চাইতে পারেন। আপনি করতে পারেন:
- কোডল্যাব প্রকল্প ডিরেক্টরি মুছুন:
rm -rf ~/task-app
- সতর্কতা ! এই পরবর্তী ক্রিয়াটি পূর্বাবস্থায় ফেরানো যাবে না! আপনি যদি স্থান খালি করতে আপনার ক্লাউড শেলের সবকিছু মুছতে চান, তাহলে আপনি আপনার পুরো হোম ডিরেক্টরি মুছে ফেলতে পারেন। সতর্ক থাকুন যে আপনি যা কিছু রাখতে চান তা অন্য কোথাও সংরক্ষণ করা হয়।
sudo rm -rf $HOME