Cloud SQL Node.js कनेक्टर का इस्तेमाल करके, Cloud SQL for PostgreSQL के साथ Cloud Run में फ़ुल स्टैक Angular ऐप्लिकेशन को डिप्लॉय करना

Cloud SQL Node.js कनेक्टर का इस्तेमाल करके, Cloud SQL for PostgreSQL के साथ Cloud Run में फ़ुल स्टैक Angular ऐप्लिकेशन को डिप्लॉय करना

इस कोडलैब (कोड बनाना सीखने के लिए ट्यूटोरियल) के बारे में जानकारी

subjectपिछली बार मार्च 28, 2025 को अपडेट किया गया
account_circleLuke Schlangen ने लिखा

1. खास जानकारी

Cloud Run एक ऐसा प्लैटफ़ॉर्म है जिसे पूरी तरह से मैनेज किया जाता है. इसकी मदद से, अपने कोड को सीधे Google के स्केलेबल इन्फ़्रास्ट्रक्चर पर चलाया जा सकता है. इस कोडलैब में, Cloud SQL Node.js कनेक्टर का इस्तेमाल करके, Cloud Run पर मौजूद Angular ऐप्लिकेशन को PostgreSQL डेटाबेस के लिए Cloud SQL से कनेक्ट करने का तरीका बताया गया है.

इस लैब में, आपको इन कामों को करने का तरीका पता चलेगा:

  • PostgreSQL के लिए Cloud SQL इंस्टेंस बनाना
  • Cloud Run पर ऐसा ऐप्लिकेशन डिप्लॉय करना जो आपके Cloud SQL डेटाबेस से कनेक्ट हो

2. ज़रूरी शर्तें

  1. अगर आपके पास पहले से कोई Google खाता नहीं है, तो आपको Google खाता बनाना होगा.
    • ऑफ़िस या स्कूल वाले खाते के बजाय, निजी खाते का इस्तेमाल करें. ऑफ़िस और स्कूल वाले खातों पर ऐसी पाबंदियां हो सकती हैं जिनकी वजह से, इस लैब के लिए ज़रूरी एपीआई चालू नहीं किए जा सकते.

3. प्रोजेक्ट सेटअप करना

  1. Google Cloud Console में साइन इन करें.
  2. Cloud Console में बिलिंग चालू करें.
    • इस लैब को पूरा करने के लिए, Cloud के संसाधनों पर 1 डॉलर से कम खर्च करना पड़ेगा.
    • आगे कोई शुल्क न देना हो, तो इस लैब के आखिर में दिए गए तरीके का इस्तेमाल करके संसाधन मिटाएं.
    • नए उपयोगकर्ताओं को 300 डॉलर का मुफ़्त क्रेडिट मिलता है.
  3. नया प्रोजेक्ट बनाएं या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल करें.

4. Cloud Shell एडिटर खोलना

  1. Cloud Shell एडिटर पर जाएं
  2. अगर टर्मिनल स्क्रीन पर सबसे नीचे नहीं दिखता है, तो उसे खोलें:
    • हैमबर्गर मेन्यू हैमबर्गर मेन्यू का आइकॉन पर क्लिक करें
    • Terminal पर क्लिक करें
    • नया टर्मिनल पर क्लिक करेंCloud Shell एडिटर में नया टर्मिनल खोलना
  3. टर्मिनल में, इस कमांड की मदद से अपना प्रोजेक्ट सेट करें:
    • फ़ॉर्मैट:
      gcloud config set project [PROJECT_ID]
    • उदाहरण:
      gcloud config set project lab-project-id-example
    • अगर आपको अपना प्रोजेक्ट आईडी याद नहीं है, तो:
      • अपने सभी प्रोजेक्ट आईडी की सूची देखने के लिए:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
      Cloud Shell Editor टर्मिनल में प्रोजेक्ट आईडी सेट करना
  4. अगर आपसे अनुमति देने के लिए कहा जाए, तो जारी रखने के लिए अनुमति दें पर क्लिक करें. Cloud Shell को अनुमति देने के लिए क्लिक करें
  5. आपको यह मैसेज दिखेगा:
    Updated property [core/project].
    
    अगर आपको WARNING दिखता है और आपसे Do you want to continue (Y/N)? पूछा जाता है, तो हो सकता है कि आपने प्रोजेक्ट आईडी गलत डाला हो. N दबाएं, Enter दबाएं, और gcloud config set project कमांड को फिर से चलाने की कोशिश करें.

5. एपीआई चालू करें

टर्मिनल में, एपीआई चालू करें:

gcloud services enable \
  sqladmin.googleapis.com \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com

अगर आपसे अनुमति देने के लिए कहा जाए, तो जारी रखने के लिए अनुमति दें पर क्लिक करें. Cloud Shell को अनुमति देने के लिए क्लिक करें

इस निर्देश को पूरा होने में कुछ मिनट लग सकते हैं. हालांकि, आखिर में आपको इस तरह का मैसेज दिखेगा:

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

6. सेवा खाता सेट अप करना

Cloud Run का इस्तेमाल करने के लिए, Google Cloud सेवा खाता बनाएं और कॉन्फ़िगर करें, ताकि उसके पास Cloud SQL से कनेक्ट करने की सही अनुमतियां हों.

  1. नया सेवा खाता बनाने के लिए, gcloud iam service-accounts create कमांड को इस तरह चलाएं:
    gcloud iam service-accounts create quickstart-service-account \
     
    --display-name="Quickstart Service Account"
  2. आपने जो Google Cloud सेवा खाता अभी बनाया है उसमें Cloud SQL क्लाइंट की भूमिका जोड़ने के लिए, gcloud projects add-iam-policy-binding कमांड को इस तरह चलाएं.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.client"
  3. आपने जो Google Cloud सेवा खाता अभी बनाया है उसमें Cloud SQL इंस्टेंस उपयोगकर्ता की भूमिका जोड़ने के लिए, gcloud projects add-iam-policy-binding कमांड को इस तरह चलाएं.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.instanceUser"
  4. आपने जो Google Cloud सेवा खाता अभी बनाया है उसमें लॉग लेखक की भूमिका जोड़ने के लिए, gcloud projects add-iam-policy-binding कमांड को इस तरह चलाएं.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/logging.logWriter"

7. Cloud SQL डेटाबेस बनाना

  1. Cloud SQL इंस्टेंस बनाने के लिए, gcloud sql instances create कमांड चलाएं
    gcloud sql instances create quickstart-instance \
        --database-version=POSTGRES_14 \
        --cpu=4 \
        --memory=16GB \
        --region=us-central1 \
        --database-flags=cloudsql.iam_authentication=on

इस निर्देश को पूरा होने में कुछ मिनट लग सकते हैं.

  1. quickstart-instance में Cloud SQL डेटाबेस बनाने के लिए, gcloud sql databases create कमांड चलाएं.
    gcloud sql databases create quickstart_db \
       
    --instance=quickstart-instance
  2. डेटाबेस को ऐक्सेस करने के लिए, पहले से बनाए गए सेवा खाते के लिए PostgreSQL डेटाबेस उपयोगकर्ता बनाएं.
    gcloud sql users create quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam \
        --instance=quickstart-instance \
        --type=cloud_iam_service_account

8. आवेदन तैयार करना

एचटीटीपी अनुरोधों का जवाब देने वाला Next.js ऐप्लिकेशन तैयार करें.

  1. task-app नाम का नया Next.js प्रोजेक्ट बनाने के लिए, इस निर्देश का इस्तेमाल करें:
    npx --yes @angular/cli@19.2.5 new task-app \
        --minimal \
        --inline-template \
        --inline-style \
        --ssr \
        --server-routing \
        --defaults
  2. डायरेक्ट्री को task-app में बदलने के लिए:
    cd task-app
  1. PostgreSQL डेटाबेस के साथ इंटरैक्ट करने के लिए, pg और Cloud SQL Node.js कनेक्टर लाइब्रेरी इंस्टॉल करें.
    npm install pg @google-cloud/cloud-sql-connector google-auth-library
  2. TypeScript Next.js ऐप्लिकेशन का इस्तेमाल करने के लिए, @types/pg को डेवलपर डिपेंडेंसी के तौर पर इंस्टॉल करें.
    npm install --save-dev @types/pg
  1. Cloud Shell Editor में server.ts फ़ाइल खोलें:
    cloudshell edit src/server.ts
    अब आपको स्क्रीन के सबसे ऊपर एक फ़ाइल दिखेगी. यहां इस server.ts फ़ाइल में बदलाव किया जा सकता है. स्क्रीन के सबसे ऊपर वाले सेक्शन में कोड दिखाएं
  2. server.ts फ़ाइल के मौजूदा कॉन्टेंट मिटाएं.
  3. नीचे दिया गया कोड कॉपी करके, खोली गई 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 pg from 'pg';
    import { AuthTypes, Connector } from '@google-cloud/cloud-sql-connector';
    import { GoogleAuth } from 'google-auth-library';
    const auth = new GoogleAuth();

    const { Pool } = pg;

    type Task = {
     
    id: string;
     
    title: string;
     
    status: 'IN_PROGRESS' | 'COMPLETE';
     
    createdAt: number;
    };

    const projectId = await auth.getProjectId();

    const connector = new Connector();
    const clientOpts = await connector.getOptions({
     
    instanceConnectionName: `${projectId}:us-central1:quickstart-instance`,
     
    authType: AuthTypes.IAM,
    });

    const pool = new Pool({
     
    ...clientOpts,
     
    user: `quickstart-service-account@${projectId}.iam`,
     
    database: 'quickstart_db',
    });

    const tableCreationIfDoesNotExist = async () => {
     
    await pool.query(`CREATE TABLE IF NOT EXISTS tasks (
         
    id SERIAL NOT NULL,
         
    created_at timestamp NOT NULL,
         
    status VARCHAR(255) NOT NULL default 'IN_PROGRESS',
         
    title VARCHAR(1024) NOT NULL,
         
    PRIMARY KEY (id)
       
    );`);
    }

    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) => {
     
    await tableCreationIfDoesNotExist();
     
    const { rows } = await pool.query(`SELECT id, created_at, status, title FROM tasks ORDER BY created_at DESC LIMIT 100`);
     
    res.send(rows);
    });

    app.post('/api/tasks', async (req, res) => {
     
    const newTaskTitle = req.body.title;
     
    if (!newTaskTitle) {
       
    res.status(400).send("Title is required");
       
    return;
     
    }
     
    await tableCreationIfDoesNotExist();
     
    await pool.query(`INSERT INTO tasks(created_at, status, title) VALUES(NOW(), 'IN_PROGRESS', $1)`, [newTaskTitle]);
     
    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 tableCreationIfDoesNotExist();
     
    await pool.query(
       
    `UPDATE tasks SET status = $1, title = $2 WHERE id = $3`,
       
    [task.status, task.title, task.id]
     
    );
     
    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 tableCreationIfDoesNotExist();
     
    await pool.query(`DELETE FROM tasks WHERE id = $1`, [task.id]);
     
    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);
  1. Cloud Shell Editor में app.component.ts फ़ाइल खोलें:
    cloudshell edit src/app/app.component.ts
    अब स्क्रीन के सबसे ऊपर मौजूदा फ़ाइल दिखेगी. यहां इस app.component.ts फ़ाइल में बदलाव किया जा सकता है. स्क्रीन के सबसे ऊपर वाले सेक्शन में कोड दिखाएं
  2. app.component.ts फ़ाइल के मौजूदा कॉन्टेंट मिटाएं.
  3. नीचे दिया गया कोड कॉपी करके, खोली गई 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();
     
    }
    }

ऐप्लिकेशन अब डिप्लॉय करने के लिए तैयार है.

9. ऐप्लिकेशन को Cloud Run पर डिप्लॉय करना

  1. अपने ऐप्लिकेशन को Cloud Run पर डिप्लॉय करने के लिए, यह कमांड चलाएं:
    gcloud run deploy to-do-tracker \
        --region=us-central1 \
        --source=. \
        --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
        --allow-unauthenticated
  2. अगर कहा जाए, तो जारी रखने के लिए Y और Enter दबाएं:
    Do you want to continue (Y/n)? Y
    

कुछ मिनटों के बाद, आपको ऐप्लिकेशन से एक यूआरएल मिलेगा.

अपने ऐप्लिकेशन को काम करते हुए देखने के लिए, यूआरएल पर जाएं. जब भी यूआरएल पर जाया जाएगा या पेज को रीफ़्रेश किया जाएगा, तब आपको टास्क ऐप्लिकेशन दिखेगा.

10. बधाई हो

इस लैब में, आपको ये काम करने का तरीका पता चला है:

  • PostgreSQL के लिए Cloud SQL इंस्टेंस बनाना
  • Cloud Run पर ऐसा ऐप्लिकेशन डिप्लॉय करना जो आपके Cloud SQL डेटाबेस से कनेक्ट हो

व्यवस्थित करें

Cloud SQL में कोई मुफ़्त टीयर नहीं है. इसका इस्तेमाल जारी रखने पर, आपसे शुल्क लिया जाएगा. अतिरिक्त शुल्कों से बचने के लिए, अपना Cloud प्रोजेक्ट मिटाया जा सकता है.

Cloud Run का इस्तेमाल न होने पर, आपसे कोई शुल्क नहीं लिया जाता. हालांकि, आर्टफ़ैक्ट रजिस्ट्री में कंटेनर इमेज को सेव करने के लिए, आपसे शुल्क लिया जा सकता है. अपने Cloud प्रोजेक्ट को मिटाने पर, उस प्रोजेक्ट में इस्तेमाल किए गए सभी संसाधनों के लिए बिलिंग बंद हो जाती है.

अगर आपको प्रोजेक्ट मिटाना है, तो:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

आपके पास अपनी CloudShell डिस्क से ग़ैर-ज़रूरी संसाधनों को मिटाने का विकल्प भी है. आप:

  1. कोडलैब प्रोजेक्ट डायरेक्ट्री मिटाएं:
    rm -rf ~/task-app
  2. चेतावनी! इस कार्रवाई को पहले जैसा नहीं किया जा सकता! अगर आपको जगह खाली करने के लिए, अपने Cloud Shell पर मौजूद सभी चीज़ें मिटानी हैं, तो अपनी पूरी होम डायरेक्ट्री मिटाएं. ध्यान रखें कि आपको जो भी चीज़ें सेव करनी हैं उन्हें किसी दूसरी जगह सेव कर लें.
    sudo rm -rf $HOME