教學課程:使用 JavaScript SDK 建置 Node.js Web 應用程式來管理 Azure Cosmos DB 中的 API for NoSQL 帳戶

適用於:NoSQL

身為開發人員,您可能有使用 NoSQL 文件資料的應用程式。 您可以在 Azure Cosmos DB 中使用 API for NoSQL 帳戶來儲存和存取此文件資料。 本 Node.js 教學課程說明如何從 Azure Cosmos DB 中的 API for NoSQL 帳戶儲存及存取資料。 本教學課程使用裝載在 Microsoft Azure App Service Web Apps 功能上的 Node.js Express 應用程式。 在本教學課程中,您會建置一個網頁型應用程式 (待辦事項清單應用程式),以便能夠建立、擷取和完成工作。 在 Azure Cosmos DB 中,這些工作會儲存為 JSON 文件。

本教學課程示範如何使用 Azure 入口網站在 Azure Cosmos DB 中建立 API for NoSQL 帳戶。 如果沒有信用卡或 Azure 訂用帳戶,您可以:

  • 設定免費的試用 Azure Cosmos DB 帳戶
  • 建置並執行建置在 Node.js SDK 上的 Web 應用程式,以建立資料庫和容器。
  • 將項目新增至容器。

本教學課程使用 JavaScript SDK 3.0 版,並涵蓋下列工作:

  • 建立 Azure Cosmos DB 帳戶
  • 建立新的 Node.js 應用程式
  • 將應用程式連線至 Azure Cosmos DB
  • 執行應用程式並將其部署至 Azure

必要條件

依照本文的指示進行之前,請確定您具備下列資源:

建立 Azure Cosmos DB 帳戶

從建立 Azure Cosmos DB 帳戶開始著手。 如果您已經擁有帳戶,或如果您使用 Azure Cosmos DB 模擬器來進行本教學課程,可以跳到建立新的 Node.js 應用程式

  1. 從 Azure 入口網站功能表或 [首頁] 頁面,選取 [建立資源]

  2. 搜尋 Azure Cosmos DB。 選取 [建立]>[Azure Cosmos DB]

  3. 在 [建立 Azure Cosmos DB 帳戶] 頁面上,選取 [Azure Cosmos DB for NoSQL ] 區段內的 [建立] 選項。

    Azure Cosmos DB 提供數個 API:

    • NoSQL (適用於文件資料)
    • PostgreSQL
    • MongoDB (適用於文件資料)
    • Apache Cassandra
    • Table
    • Apache Gremlin (適用於圖形資料)

    若要深入了解 API for NoSQL,請參閱歡迎使用 Azure Cosmos DB

  4. 在 [建立 Azure Cosmos DB 帳戶] 頁面中,輸入新 Azure Cosmos DB 帳戶的基本設定。

    設定 Description
    訂用帳戶 訂用帳戶名稱 選取您要用於此 Azure Cosmos DB 帳戶的 Azure 訂用帳戶。
    資源群組 資源群組名稱 選取資源群組,或選取 [新建],然後輸入新資源群組的唯一名稱。
    客戶名稱 唯一名稱 輸入名稱來識別您的 Azure Cosmos DB 帳戶。 因為 documents.azure.com 會附加到您所提供的名稱以建立 URI,請使用唯一名稱。 名稱只能包含小寫字母、數字及連字號 (-) 字元。 其必須是 3-44 個字元。
    Location 最接近使用者的區域 選取用來裝載 Azure Cosmos DB 帳戶的地理位置。 使用最接近使用者的位置,讓他們能以最快速度存取資料。
    容量模式 佈建輸送量無伺服器 選取 [佈建的輸送量],以佈建的輸送量模式建立帳戶。 選取 [無伺服器],以無伺服器模式建立帳戶。
    申請 Azure Cosmos DB 免費階層折扣 適用不適用 使用 Azure Cosmos DB 免費層,您便能在帳戶中免費取得前 1000 RU/秒和 25 GB 的儲存體。 深入了解免費層
    限制帳戶總輸送量 是否選取 限制可在此帳戶上佈建的總輸送量。 此限制可防止與佈建輸送量相關的非預期費用。 建立您的帳戶之後,您可以更新或移除此限制。

    每個 Azure 訂用帳戶最多可以有一個免費層的 Azure Cosmos DB 帳戶,而且必須在建立帳戶時選擇加入。 若您並未看到套用免費層折扣的選項,則訂用帳戶中的另一個帳戶已透過免費層啟用。

    Screenshot shows the Create Azure Cosmos DB Account page.

    注意

    如果您選取 [無伺服器] 作為容量模式,則無法使用下列選項:

    • 套用免費層折扣
    • 限制帳戶總輸送量
  5. 在 [全域散發] 索引標籤中,設定下列詳細資料。 您可以保留預設值以用於本快速入門:

    設定 Description
    異地備援 停用 藉由將您的區域與配對區域進行配對,在您的帳戶上啟用或停用全域散發。 您可以在稍後將更多區域新增至您的帳戶。
    多重區域寫入 停用 多重區域寫入功能可讓您利用在全球為資料庫及容器佈建的輸送量。
    可用性區域 停用 可用性區域可協助您進一步改善應用程式的可用性和復原能力。

    注意

    如果您在上一個 [基本] 頁面中選取 [無伺服器] 作為 [容量模式],則無法使用下列選項:

    • 異地備援
    • 多重區域寫入
  6. 您可以選擇性在下列索引標籤中設定其他詳細資料:

    • 網路功能。 設定從虛擬網路存取
    • 備份原則。 設定定期連續備份原則。
    • 加密。 使用服務受控金鑰或客戶自控金鑰
    • 標籤。 籤標籤為成對的名稱和數值,可讓您透過將相同標籤套用至多個資源與資源群組,進而分類資源並檢視合併的帳單。
  7. 選取 [檢閱 + 建立]。

  8. 檢閱帳戶設定,然後選取 [建立]。 建立帳戶需要幾分鐘的時間。 請等候入口網站頁面顯示 [您的部署已完成]

    Screenshot shows that your deployment is complete.

  9. 選取 [前往資源] 前往 Azure Cosmos DB 帳戶頁面。

    Screenshot shows the Azure Cosmos DB account page.

移至 Azure Cosmos DB 帳戶頁面,然後選取 [金鑰]。 複製要在您接下來建立的 Web 應用程式中使用的值。

Screenshot of the Azure portal with the Keys button highlighted on the Azure Cosmos DB account page

建立新的 Node.js 應用程式

現在,了解如何使用 Express 架構來建立基本的 Hello World Node.js 專案。

  1. 開啟您喜好的終端機,例如 Node.js 命令提示字元。

  2. 瀏覽至您想要在其中儲存新應用程式的目錄。

  3. 使用 Express 產生器來產生名為 todo的新應用程式。

    express todo
    
  4. 開啟 todo 目錄並安裝相依性。

    cd todo
    npm install
    
  5. 執行新的應用程式。

    npm start
    
  6. 若要在瀏覽器中檢視新的應用程式,請移至 http://localhost:3000

    Screenshot of the Hello World application in a browser window.

    在終端機視窗中使用 CTRL+C 停止應用程式,然後選取 y 以終止批次作業。

安裝必要模組

package.json 檔案是建立在專案根目錄中的其中一個檔案。 此檔案包含 Node.js 應用程式所需其他模組的清單。 當您將此應用程式部署至 Azure 時,此檔案可用來決定 Azure 上應安裝哪些模組才能支援您的應用程式。 再安裝兩個本教學課程所需的套件。

  1. 透過 npm 安裝 @azure/cosmos 模組。

    npm install @azure/cosmos
    

將 Node.js 應用程式連線至 Azure Cosmos DB

完成初始安裝和設定之後,了解如何撰寫待辦事項應用程式與 Azure Cosmos DB 通訊所需的程式碼。

建立模型

  1. 在專案目錄的根目錄中,建立一個名為 models 的新目錄。

  2. models 目錄中,建立名為 taskDao.js 的新檔案。 此檔案包含建立資料庫和容器所需的程式碼。 其中也會定義用來在 Azure Cosmos DB 中讀取、更新、建立和尋找工作的方法。

  3. 將下列程式碼複製到 taskDao.js 檔案:

     // @ts-check
     const CosmosClient = require('@azure/cosmos').CosmosClient
     const debug = require('debug')('todo:taskDao')
    
     // For simplicity we'll set a constant partition key
     const partitionKey = undefined
     class TaskDao {
       /**
        * Manages reading, adding, and updating Tasks in Azure Cosmos DB
        * @param {CosmosClient} cosmosClient
        * @param {string} databaseId
        * @param {string} containerId
        */
       constructor(cosmosClient, databaseId, containerId) {
         this.client = cosmosClient
         this.databaseId = databaseId
         this.collectionId = containerId
    
         this.database = null
         this.container = null
       }
    
       async init() {
         debug('Setting up the database...')
         const dbResponse = await this.client.databases.createIfNotExists({
           id: this.databaseId
         })
         this.database = dbResponse.database
         debug('Setting up the database...done!')
         debug('Setting up the container...')
         const coResponse = await this.database.containers.createIfNotExists({
           id: this.collectionId
         })
         this.container = coResponse.container
         debug('Setting up the container...done!')
       }
    
       async find(querySpec) {
         debug('Querying for items from the database')
         if (!this.container) {
           throw new Error('Collection is not initialized.')
         }
         const { resources } = await this.container.items.query(querySpec).fetchAll()
         return resources
       }
    
       async addItem(item) {
         debug('Adding an item to the database')
         item.date = Date.now()
         item.completed = false
         const { resource: doc } = await this.container.items.create(item)
         return doc
       }
    
       async updateItem(itemId) {
         debug('Update an item in the database')
         const doc = await this.getItem(itemId)
         doc.completed = true
    
         const { resource: replaced } = await this.container
           .item(itemId, partitionKey)
           .replace(doc)
         return replaced
       }
    
       async getItem(itemId) {
         debug('Getting an item from the database')
         const { resource } = await this.container.item(itemId, partitionKey).read()
         return resource
       }
     }
    
     module.exports = TaskDao
    
  4. 儲存並關閉 taskDao.js 檔案。

建立控制器

  1. 在專案的 routes 目錄中,建立名為 tasklist.js 的新檔案。

  2. tasklist.js中加入以下程式碼。 此程式碼會載入供 tasklist.js 使用的 CosmosClient 和 async 模組。 此程式碼也會定義 TaskList 類別,此類別會傳遞作為我們稍早定義的 TaskDao 物件執行個體:

     const TaskDao = require("../models/TaskDao");
    
     class TaskList {
       /**
        * Handles the various APIs for displaying and managing tasks
        * @param {TaskDao} taskDao
        */
       constructor(taskDao) {
         this.taskDao = taskDao;
       }
       async showTasks(req, res) {
         const querySpec = {
           query: "SELECT * FROM root r WHERE r.completed=@completed",
           parameters: [
             {
               name: "@completed",
               value: false
             }
           ]
         };
    
         const items = await this.taskDao.find(querySpec);
         res.render("index", {
           title: "My ToDo List ",
           tasks: items
         });
       }
    
       async addTask(req, res) {
         const item = req.body;
    
         await this.taskDao.addItem(item);
         res.redirect("/");
       }
    
       async completeTask(req, res) {
         const completedTasks = Object.keys(req.body);
         const tasks = [];
    
         completedTasks.forEach(task => {
           tasks.push(this.taskDao.updateItem(task));
         });
    
         await Promise.all(tasks);
    
         res.redirect("/");
       }
     }
    
     module.exports = TaskList;
    
  3. 儲存並關閉 tasklist.js 檔案。

新增 config.js

  1. 在專案目錄的根目錄中,建立名為 config.js 的新檔案。

  2. 將下列程式碼新增至 config.js 檔案。 此程式碼會定義組態設定和應用程式所需的值。

    const config = {};
    
    config.host = process.env.HOST || "[the endpoint URI of your Azure Cosmos DB account]";
    config.authKey =
      process.env.AUTH_KEY || "[the PRIMARY KEY value of your Azure Cosmos DB account";
    config.databaseId = "ToDoList";
    config.containerId = "Items";
    
    if (config.host.includes("https://localhost:")) {
      console.log("Local environment detected");
      console.log("WARNING: Disabled checking of self-signed certs. Do not have this code in production.");
      process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
      console.log(`Go to http://localhost:${process.env.PORT || '3000'} to try the sample.`);
    }
    
    module.exports = config;
    
  3. 在 config.js 檔案中,使用 Azure 入口網站上 Azure Cosmos DB 帳戶的 [金鑰] 頁面中找到的值,更新 HOST 和 AUTH_KEY 的值。

  4. 儲存並關閉 config.js 檔案。

修改 app.js

  1. 在專案目錄中,開啟 app.js 檔案。 這是稍早建立 Express Web 應用程式時所建立的檔案。

  2. 將下列程式碼新增至 app.js 檔案。 此程式碼會定義要使用的組態檔,並將值載入您在後續章節將用到的某些變數中。

     const CosmosClient = require('@azure/cosmos').CosmosClient
     const config = require('./config')
     const TaskList = require('./routes/tasklist')
     const TaskDao = require('./models/taskDao')
    
     const express = require('express')
     const path = require('path')
     const logger = require('morgan')
     const cookieParser = require('cookie-parser')
     const bodyParser = require('body-parser')
    
     const app = express()
    
     // view engine setup
     app.set('views', path.join(__dirname, 'views'))
     app.set('view engine', 'jade')
    
     // uncomment after placing your favicon in /public
     //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
     app.use(logger('dev'))
     app.use(bodyParser.json())
     app.use(bodyParser.urlencoded({ extended: false }))
     app.use(cookieParser())
     app.use(express.static(path.join(__dirname, 'public')))
    
     //Todo App:
     const cosmosClient = new CosmosClient({
       endpoint: config.host,
       key: config.authKey
     })
     const taskDao = new TaskDao(cosmosClient, config.databaseId, config.containerId)
     const taskList = new TaskList(taskDao)
     taskDao
       .init(err => {
         console.error(err)
       })
       .catch(err => {
         console.error(err)
         console.error(
           'Shutting down because there was an error settinig up the database.'
         )
         process.exit(1)
       })
    
     app.get('/', (req, res, next) => taskList.showTasks(req, res).catch(next))
     app.post('/addtask', (req, res, next) => taskList.addTask(req, res).catch(next))
     app.post('/completetask', (req, res, next) =>
       taskList.completeTask(req, res).catch(next)
     )
     app.set('view engine', 'jade')
    
     // catch 404 and forward to error handler
     app.use(function(req, res, next) {
       const err = new Error('Not Found')
       err.status = 404
       next(err)
     })
    
     // error handler
     app.use(function(err, req, res, next) {
       // set locals, only providing error in development
       res.locals.message = err.message
       res.locals.error = req.app.get('env') === 'development' ? err : {}
    
       // render the error page
       res.status(err.status || 500)
       res.render('error')
     })
    
     module.exports = app
    
  3. 最後,儲存並關閉 app.js 檔案。

建置使用者介面

現在,建置使用者介面,讓使用者可以與應用程式互動。 您在前幾節中建立的 Express 應用程式使用 Jade 作為檢視引擎。

  1. views 目錄中的 layout.jade 檔是用來作為其他 .jade 檔案的全域範本。 在此步驟中,您會將其修改為使用 Twitter Bootstrap,這是用來設計網站的工具組。

  2. 開啟在 views 資料夾中找到的 layout.jade 檔案,並將其中的內容取代為下列程式碼:

    doctype html
    html
      head
        title= title
        link(rel='stylesheet', href='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/css/bootstrap.min.css')
        link(rel='stylesheet', href='/stylesheets/style.css')
      body
        nav.navbar.navbar-inverse.navbar-fixed-top
          div.navbar-header
            a.navbar-brand(href='#') My Tasks
        block content
        script(src='//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.2.min.js')
        script(src='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/bootstrap.min.js')
    

    此程式碼會指示 Jade 引擎轉譯出應用程式的部分 HTML,並建立稱為 content區塊,讓您能在其中提供內容頁面的配置。 儲存並關閉 layout.jade 檔案。

  3. 開啟 index.jade 檔案,這是應用程式所使用的檢視。 使用下列程式碼取代檔案的內容:

    extends layout
    block content
         h1 #{title}
         br
    
         form(action="/completetask", method="post")
          table.table.table-striped.table-bordered
             tr
               td Name
               td Category
               td Date
               td Complete
             if (typeof tasks === "undefined")
               tr
                 td
             else
               each task in tasks
                 tr
                   td #{task.name}
                   td #{task.category}
                   - var date  = new Date(task.date);
                   - var day   = date.getDate();
                   - var month = date.getMonth() + 1;
                   - var year  = date.getFullYear();
                   td #{month + "/" + day + "/" + year}
                   td
                    if(task.completed) 
                     input(type="checkbox", name="#{task.id}", value="#{!task.completed}", checked=task.completed)
                    else
                     input(type="checkbox", name="#{task.id}", value="#{!task.completed}", checked=task.completed)
           button.btn.btn-primary(type="submit") Update tasks
         hr
         form.well(action="/addtask", method="post")
           label Item Name:
           input(name="name", type="textbox")
           label Item Category:
           input(name="category", type="textbox")
           br
           button.btn(type="submit") Add item
    

此程式碼會擴充配置,並為您在 layout.jade 檔案中看到的 content 預留位置提供內容。 在該版面配置中,您已建立兩個 HTML 表單。

第一個表單包含資料的表格,以及可讓您透過張貼到控制器的 /completeTask 方法來更新項目的按鈕。

第二個表單包含兩個輸入欄位和一個按鈕,該按鈕可讓您透過張貼到控制器的 /addtask 方法來建立新項目,這是讓應用程式運作所需的一切。

在本機執行您的應用程式

建立應用程式之後,您就可以使用下列步驟在本機執行此應用程式:

  1. 若要在本機電腦上測試應用程式,請在終端機中執行 npm start 以啟動應用程式,然後重新整理 http://localhost:3000 頁面。 該頁面現在看起來應該類似下列螢幕擷取畫面:

    Screenshot of the My Todo List application in a browser.

    提示

    如果您收到有關 layout.jade 檔案或 index.jade 檔案縮排的錯誤,請確定這兩個檔案的前兩行靠左對齊 (沒有空格)。 如果前兩行之前有空格,請將空格移除,儲存這兩個檔案,然後重新整理瀏覽器視窗。

  2. 使用 [項目名稱] 和 [項目類別目錄] 欄位來輸入新工作,然後選取 [新增項目],以使用這些屬性在 Azure Cosmos DB 中建立文件。

  3. 此頁面應會更新,以在 [待辦事項] 清單中顯示新建立的項目。

    Screenshot of the application with a new item in the ToDo list.

  4. 若要完成工作,請選取 [完成] 資料行中的核取方塊,然後選取 [更新工作] 以更新您已建立的文件,並將其從檢視中移除。

  5. 若要停止應用程式,請在終端機視窗中按 CTRL+C,然後選取 y 以終止批次作業。

將應用程式部署至 App Service

應用程式在本機成功執行之後,就可以部署至 Azure App Service。 在終端機,請確定您在 todo 應用程式目錄中。 使用下列 az webapp up 命令,在本機資料夾 (todo) 中部署程式碼:

az webapp up --sku F1 --name <app-name>

將 <app_name> 換成整個 Azure 中唯一的名稱 (有效字元為 a-z、0-9 和 -)。 良好的模式是使用您的公司名稱和應用程式識別碼的組合。 若要深入了解應用程式部署,請參閱 Azure 中的 Node.js 應用程式部署

此命令可能需要幾分鐘才能完成。 此命令會提供相關訊息,包括建立資源群組、App Service 方案和應用程式資源、設定記錄,以及執行 ZIP 部署。 此命令會在執行時提供這些訊息。 然後提供 URL 讓您在 http://<app-name>.azurewebsites.net 上啟動應用程式,此為應用程式在 Azure 上的 URL。

清除資源

若不再需要這些資源,您可以刪除資源群組、Azure Cosmos DB 帳戶和所有相關資源。 請選取用於 Azure Cosmos DB 帳戶的資源群組,選取 [刪除],然後確認要刪除的資源群組名稱,即可刪除資源。

下一步

您可以使用現有資料庫叢集的相關資訊進行容量規劃。