教程:使用 JavaScript SDK 生成 Node.js Web 应用,以便在 Azure Cosmos DB 中管理 SQL API 帐户Tutorial: Build a Node.js web app using the JavaScript SDK to manage a SQL API account in Azure Cosmos DB

作为开发人员,你可能有使用 NoSQL 文件数据的应用程序。As a developer, you might have applications that use NoSQL document data. 可以使用 Azure Cosmos DB 中的 SQL API 帐户存储和访问此文档数据。You can use a SQL API account in Azure Cosmos DB to store and access this document data. 本 Node.js 教程介绍了如何使用在 Azure 应用服务的 Web 应用功能上托管的 Node.js Express 应用程序,通过 Azure Cosmos DB 中的 SQL API 帐户存储和访问数据。This Node.js tutorial shows you how to store and access data from a SQL API account in Azure Cosmos DB by using a Node.js Express application that is hosted on the Web Apps feature of Azure App Service. 在本教程中,需生成一个基于 Web 的应用程序(待办事项应用)来创建、检索和完成任务。In this tutorial, you will build a web-based application (Todo app) that allows you to create, retrieve, and complete tasks. 任务存储为 Azure Cosmos DB 中的 JSON 文档。The tasks are stored as JSON documents in Azure Cosmos DB.

本教程演示如何使用 Azure 门户在 Azure Cosmos DB 中创建 SQL API 帐户,This tutorial demonstrates how to create a SQL API account in Azure Cosmos DB by using the Azure portal. 然后生成并运行一个基于 Node.js SDK 的 Web 应用,以便创建数据库和容器并向容器添加项。You then build and run a web app that is built on the Node.js SDK to create a database and container, and add items to the container. 本教程使用 JavaScript SDK 版本 3.0。This tutorial uses JavaScript SDK version 3.0.

本教程涵盖以下任务:This tutorial covers the following tasks:

  • 创建 Azure Cosmos DB 帐户Create an Azure Cosmos DB account
  • 创建新的 Node.js 应用程序Create a new Node.js application
  • 将应用程序连接到 Azure Cosmos DBConnect the application to Azure Cosmos DB
  • 运行应用程序并将其部署到 AzureRun and deploy the application to Azure

必备条件Prerequisites

在按照本文中的说明操作之前,请确保具备以下资源:Before following the instructions in this article, ensure that you have the following resources:

创建 Azure Cosmos DB 帐户Create an Azure Cosmos DB account

首先创建一个 Azure Cosmos DB 帐户。Let's start by creating an Azure Cosmos DB account. 如果已有一个帐户,或者要在本教程中使用 Azure Cosmos DB 模拟器,可以跳到步骤 2:创建新的 Node.js 应用程序If you already have an account or if you are using the Azure Cosmos DB Emulator for this tutorial, you can skip to Step 2: Create a new Node.js application.

  1. 转到 Azure 门户以创建 Azure Cosmos DB 帐户。Go to the Azure portal to create an Azure Cosmos DB account. 搜索“Azure Cosmos DB”,然后选择它。 。Search for and select Azure Cosmos DB.

    Azure 门户“数据库”窗格

  2. 选择“添加” 。Select Add.

  3. 在“创建 Azure Cosmos DB 帐户”页上,输入新 Azure Cosmos 帐户的基本设置 。On the Create Azure Cosmos DB Account page, enter the basic settings for the new Azure Cosmos account.

    设置Setting Value DescriptionDescription
    订阅Subscription 订阅名称Subscription name 选择要用于此 Azure Cosmos 帐户的 Azure 订阅。Select the Azure subscription that you want to use for this Azure Cosmos account.
    资源组Resource Group 资源组名称Resource group name 选择一个资源组,或者选择“新建”,然后输入新资源组的唯一名称。 Select a resource group, or select Create new, then enter a unique name for the new resource group.
    帐户名Account Name 唯一的名称A unique name 输入标识此 Azure Cosmos 帐户的名称。Enter a name to identify your Azure Cosmos account. 由于 documents.azure.cn 字符串将追加到所提供的 ID 后面以创建 URI,因此,请使用唯一的 ID。Because documents.azure.cn is appended to the ID that you provide to create your URI, use a unique ID.

    ID 只能包含小写字母、数字和连字符 (-) 字符。The ID can only contain lowercase letters, numbers, and the hyphen (-) character. 它的长度必须介于 3 到 31 个字符之间。It must be between 3-31 characters in length.
    APIAPI 要创建的帐户的类型The type of account to create 选择“Core (SQL)”,以便使用 SQL 语法创建文档数据库并进行查询 。Select Core (SQL) to create a document database and query by using SQL syntax.

    API 确定要创建的帐户的类型。The API determines the type of account to create. Azure Cosmos DB 提供五种 API:适用于文档数据的 Core (SQL) 和 MongoDB、适用于图形数据的 Gremlin、Azure 表和 Cassandra。Azure Cosmos DB provides five APIs: Core (SQL) and MongoDB for document data, Gremlin for graph data, Azure Table, and Cassandra. 目前,你必须为每种 API 创建单独的帐户。Currently, you must create a separate account for each API.

    详细了解 SQL APILearn more about the SQL API.
    位置Location 离用户最近的区域The region closest to your users 选择用于托管 Azure Cosmos DB 帐户的地理位置。Select a geographic location to host your Azure Cosmos DB account. 使用离用户最近的位置,使他们能够以最快的速度访问数据。Use the location that is closest to your users to give them the fastest access to the data.

    Azure Cosmos DB 的“新建帐户”页

  4. 选择“查看 + 创建” 。Select Review + create. 可以跳过“网络”和“标记”部分 。You can skip the Network and Tags sections.

  5. 检查帐户设置,然后选择“创建”。 Review the account settings, and then select Create. 创建帐户需要几分钟时间。It takes a few minutes to create the account. 等待门户页显示“你的部署已完成” 消息。Wait for the portal page to display Your deployment is complete.

    Azure 门户“通知”窗格

  6. 选择“转到资源”,转到 Azure Cosmos DB 帐户页。 Select Go to resource to go to the Azure Cosmos DB account page.

    Azure Cosmos DB 帐户页

转到 Azure Cosmos DB 帐户页,并选择“密钥” 。Go to the Azure Cosmos DB account page, and select Keys. 复制要在下一步创建的 Web 应用程序中使用的值。Copy the values to use in the web application you create next.

Azure 门户的 Azure Cosmos DB 帐户页上突出显示“密钥”按钮的屏幕截图

创建新的 Node.js 应用程序Create a new Node.js application

现在让我们来了解如何使用 Express 框架创建基本的 Hello World Node.js 项目。Now let's learn to create a basic Hello World Node.js project using the Express framework.

  1. 打开首选终端,例如 Node.js 命令提示符。Open your favorite terminal, such as the Node.js command prompt.

  2. 导航到要在其中存储新应用程序的目录。Navigate to the directory in which you'd like to store the new application.

  3. 使用 Express 生成器生成名叫 todo的新应用程序。Use the express generator to generate a new application called todo.

    express todo
    
  4. 打开 todo 目录并安装依赖项。Open the todo directory and install dependencies.

    cd todo
    npm install
    
  5. 运行新应用程序。Run the new application.

    npm start
    
  6. 在浏览器中导航到 http://localhost:3000 即可查看新应用程序。You can view your new application by navigating your browser to http://localhost:3000.

    了解 Node.js - 浏览器窗口中 Hello World 应用程序的屏幕截图

    停止应用程序的方法是先在终端窗口中按 CTRL+C,然后选择“y”终止批处理作业。 Stop the application by using CTRL+C in the terminal window, and select y to terminate the batch job.

安装所需的模块Install the required modules

package.json 文件是在项目的根目录中创建的文件之一。The package.json file is one of the files created in the root of the project. 此文件包含一系列其他模块,它们是 Node.js 应用程序必需的。This file contains a list of additional modules that are required for your Node.js application. 在将此应用程序部署到 Azure 时,请使用此文件以确定应该在 Azure 上安装哪些模块来支持应用程序。When you deploy this application to Azure, this file is used to determine which modules should be installed on Azure to support your application. 为本教程再安装两个包。Install two more packages for this tutorial.

  1. 通过 npm 安装 @azure/cosmos 模块。Install the @azure/cosmos module via npm.

    npm install @azure/cosmos
    

将 Node.js 应用程序连接到 Azure Cosmos DBConnect the Node.js application to Azure Cosmos DB

完成初始安装和配置以后,下一步是编写待办事项应用程序与 Azure Cosmos DB 通信所需的代码。Now that you have completed the initial setup and configuration, next you will write code that is required by the todo application to communicate with Azure Cosmos DB.

创建模型Create the model

  1. 在项目的根目录中,创建名为 models的新目录。At the root of your project directory, create a new directory named models.

  2. models 目录中,创建一个名为 taskDao.js 的新文件。In the models directory, create a new file named taskDao.js. 此文件包含创建数据库和容器所需的代码,This file contains code required to create the database and container. 并定义在 Azure Cosmos DB 中读取、更新、创建和查找任务所需的方法。It also defines methods to read, update, create, and find tasks in Azure Cosmos DB.

  3. 将以下代码复制到“taskDao.js” 文件中:Copy the following code into the taskDao.js file:

    // @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 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 文件。Save and close the taskDao.js file.

创建控制器Create the controller

  1. 在项目的 routes 目录中,创建一个名为 tasklist.js 的新文件。In the routes directory of your project, create a new file named tasklist.js.

  2. 将以下代码添加到 tasklist.jsAdd the following code to tasklist.js. 此代码会加载 tasklist.js 使用的 CosmosClient 和 async 模块,This code loads the CosmosClient and async modules, which are used by tasklist.js. 并定义 TaskList 类,该类作为我们之前定义的 TaskDao 对象的一个实例来传递:This code also defines the TaskList class, which is passed as an instance of the TaskDao object we defined earlier:

    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 文件。Save and close the tasklist.js file.

添加 config.jsAdd config.js

  1. 在项目根目录中创建一个名为 config.js 的新文件。At the root of your project directory, create a new file named config.js.

  2. 将以下代码添加到 config.js 文件。Add the following code to config.js file. 此代码会定义应用程序所需的配置设置和值。This code defines configuration settings and values needed for our application.

    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 的值。In the config.js file, update the values of HOST and AUTH_KEY using the values found in the Keys page of your Azure Cosmos DB account on the Azure portal.

  4. 保存并关闭 config.js 文件。Save and close the config.js file.

修改 app.jsModify app.js

  1. 在项目目录中,打开 app.js 文件。In the project directory, open the app.js file. 此文件早于 Express Web 应用程序创建。This file was created earlier when the Express web application was created.

  2. 将以下代码添加到 app.js 文件。Add the following code to the app.js file. 此代码定义要使用的配置文件,并将值加载到将要在后续部分使用的某些变量中。This code defines the config file to be used, and loads the values into some variables that you will use in the next sections.

    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 文件。Finally, save and close the app.js file.

生成用户界面Build a user interface

现在让我们生成用户界面,使用户能够与应用程序交互。Now let's build the user interface so that a user can interact with the application. 我们在前述部分创建的 Express 应用程序使用 Jade 作为视图引擎。The Express application we created in the previous sections uses Jade as the view engine.

  1. views 目录中的 layout.jade 文件用作其他 .jade 文件的全局模板。The layout.jade file in the views directory is used as a global template for other .jade files. 在此步骤中,将对其进行修改,让其使用 Twitter Bootstrap(一个用于设计网站的工具包)。In this step you will modify it to use Twitter Bootstrap, which is a toolkit used to design a website.

  2. 打开 views 文件夹中的 layout.jade 文件,将内容替换为以下代码:Open the layout.jade file found in the views folder and replace the contents with the following code:

    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,这样我们就可以在其中提供内容页面的布局。This code tells the Jade engine to render some HTML for our application, and creates a block called content where we can supply the layout for our content pages. 保存并关闭 layout.jade 文件。Save and close the layout.jade file.

  3. 现在打开 index.jade 文件(应用程序将要使用的视图),并将文件内容替换为以下代码:Now open the index.jade file, the view that will be used by our application, and replace the content of the file with the following code:

    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 占位符提供内容。This code extends layout, and provides content for the content placeholder we saw in the layout.jade file earlier. 在此布局中,我们创建了两个 HTML 窗体。In this layout, we created two HTML forms.

第一个窗体中的表包含数据和按钮,可以通过该按钮将内容发布到控制器的 /completeTask 方法,以便更新项。The first form contains a table for your data and a button that allows you to update items by posting to /completeTask method of the controller.

第二个窗体包含两个输入字段和一个按钮,可以通过该按钮将内容发布到控制器的 /addtask 方法,以便新建项。The second form contains two input fields and a button that allows you to create a new item by posting to /addtask method of the controller. 这是运行应用程序所需的一切。That's all we need for the application to work.

在本地运行应用程序Run your application locally

生成应用程序以后,即可通过以下步骤在本地运行它:Now that you have built the application, you can run it locally by using the following steps:

  1. 若要在本地计算机上测试应用程序,请在终端中运行 npm start 以启动应用程序,然后刷新 http://localhost:3000 浏览器页。To test the application on your local machine, run npm start in the terminal to start your application, and then refresh the http://localhost:3000 browser page. 该页现在应该如以下屏幕截图所示:The page should now look as shown in the following screenshot:

    浏览器窗口中 MyTodo List 应用程序的屏幕截图

    提示

    如果收到有关 layout.jade 文件或 index.jade 文件的缩进错误,请确保这两个文件中的头两行都已经左对齐,没有空格。If you receive an error about the indent in the layout.jade file or the index.jade file, ensure that the first two lines in both files are left-justified, with no spaces. 如果头两行之前留有空格,请删除这些空格,将这两个文件保存,然后刷新浏览器窗口。If there are spaces before the first two lines, remove them, save both files, and then refresh your browser window.

  2. 使用“项”、“项名”和“类别”字段输入新任务,然后选择“添加项”。 Use the Item, Item Name, and Category fields to enter a new task, and then select Add Item. 此时会在 Azure Cosmos DB 中创建具有这些属性的文档。It creates a document in Azure Cosmos DB with those properties.

  3. 页面应更新为在 ToDo 列表中显示新建项。The page should update to display the newly created item in the ToDo list.

    ToDo 列表中具有新项的应用程序的屏幕截图

  4. 若要完成任务,请选中“完成”列中的复选框,然后选择“更新任务” 。To complete a task, select the check box in the Complete column, and then select Update tasks. 此时会更新已创建的文档并将其从视图中删除。It updates the document you already created and removes it from the view.

  5. 若要停止应用程序,请在终端窗口中按 CTRL+C,然后选择“Y”以终止批处理作业。 To stop the application, press CTRL+C in the terminal window and then select Y to terminate the batch job.

将应用程序部署到 Web 应用Deploy your application to Web Apps

应用程序在本地成功以后,即可通过以下步骤将其部署到 Azure:After your application succeeds locally, you can deploy it to Azure by using the following steps:

  1. 启用 Web 应用应用程序的 git 存储库(如果尚未启用)。If you haven't already done so, enable a git repository for your Web Apps application.

  2. 将 Web 应用应用程序添加为 git 远程。Add your Web Apps application as a git remote.

    git remote add azure https://username@your-azure-website.scm.chinacloudsites.cn:443/your-azure-website.git
    
  3. 通过将应用程序推送到远程群集来部署应用程序。Deploy the application by pushing it to the remote.

    git push azure master
    
  4. 几秒钟后,Web 应用程序就会发布完毕并在浏览器中启动。In a few seconds, your web application is published and launched in a browser.

清理资源Clean up resources

不再需要资源组、Azure Cosmos DB 帐户和所有相关的资源时,可将这些资源删除。When these resources are no longer needed, you can delete the resource group, Azure Cosmos DB account, and all the related resources. 为此,请选择用于 Azure Cosmos DB 帐户的资源组,接着选择“删除” ,然后确认要删除的资源组的名称。To do so, select the resource group that you used for the Azure Cosmos DB account, select Delete, and then confirm the name of the resource group to delete.

后续步骤Next steps