教程:使用 DocumentDB 生成 TypeScript 应用并部署到 Azure DocumentDB

在本教程中,你将 DocumentDB 用作 Docker 容器中的本地开发数据库,使用 MongoDB 驱动程序生成 TypeScript 应用程序,然后通过交换连接字符串迁移到 Azure DocumentDB。

在本教程中,你将了解:

  • 在 Docker 容器中启动 DocumentDB
  • 创建 TypeScript 应用程序
  • 将应用程序连接到本地数据库
  • 创建 Azure DocumentDB 群集
  • 将应用程序部署到 Azure DocumentDB

先决条件

启动容器

使用 DocumentDB 的 Docker 容器映像创建本地开发数据库。

  1. 拉取 DocumentDB 容器映像。

    docker pull ghcr.io/documentdb/documentdb/documentdb-local:latest
    
  2. 使用用户名和密码启动容器。

    docker run --detach --publish 10260:10260 --name documentdb ghcr.io/documentdb/documentdb/documentdb-local:latest --username <admin-username> --password <admin-password>
    

    Important

    <admin-username><admin-password>替换为你自己的凭据。

  3. 验证容器是否正在运行。

    docker ps --filter "name=documentdb"
    

    Important

    DocumentDB 网关终结点在端口 10260上可用。 使用以下“连接字符串”格式进行连接:

    mongodb://<admin-username>:<admin-password>@localhost:10260/?tls=true&tlsAllowInvalidCertificates=true
    
    设置 价值
    终结点 https://localhost:10260
    用户名 <admin-username>
    密码 <admin-password>
    TLS true
    TLS 允许无效证书 true

    <admin-username><admin-password>替换为你自己的凭据。

创建应用程序

创建 Next.js 应用程序并将其连接到本地数据库。

  1. 在空文件夹中创建新的 Next.js 应用程序。

    npx create-next-app .
    
  2. 安装 Node.js MongoDB 驱动程序。

    npm install --save mongodb
    
  3. 使用以下代码创建一 app/data.tsx 个名为的新文件。 此文件包含与数据库交互的服务器操作。

    "use server";
    
    import { MongoClient, ObjectId } from "mongodb";
    import { revalidatePath } from "next/cache";
    
    const connectionString = "<documentdb-connection-string>";
    
    const client = new MongoClient(connectionString);
    const db = client.db("work-management");
    const collection = db.collection("tasks");
    
    export type Todo = {
      id: string;
      title: string;
      completed: boolean;
    };
    
    export async function getTodos(): Promise<Todo[]> {
      const docs = await collection.find().sort({ _id: -1 }).toArray();
      return docs.map((doc) => ({
        id: doc._id.toString(),
        title: doc.title,
        completed: doc.completed,
      }));
    }
    
    export async function addTodo(formData: FormData) {
      const title = formData.get("title") as string;
      if (!title?.trim()) return;
      await collection.insertOne({ title: title.trim(), completed: false });
      revalidatePath("/");
    }
    
    export async function toggleTodo(formData: FormData) {
      const id = formData.get("id") as string;
      const doc = await collection.findOne({ _id: new ObjectId(id) });
      if (doc) {
        await collection.updateOne(
          { _id: new ObjectId(id) },
          { $set: { completed: !doc.completed } }
        );
      }
      revalidatePath("/");
    }
    
    export async function deleteTodo(formData: FormData) {
      const id = formData.get("id") as string;
      await collection.deleteOne({ _id: new ObjectId(id) });
      revalidatePath("/");
    }
    

    Important

    在启动容器时,请将连接字符串占位符替换为本教程前面指定的凭据。

  4. 删除现有 app/page.tsx 文件并将其替换为以下代码。

    import { getTodos, addTodo, toggleTodo, deleteTodo } from "./data";
    
    export default async function Home() {
      const todos = await getTodos();
    
      return (
        <div className="min-h-screen bg-black text-zinc-50 font-sans">
          <main className="max-w-lg mx-auto py-16 px-8">
            <h1 className="text-2xl font-semibold tracking-tight mb-8">Todo App</h1>
    
            <form action={addTodo} className="flex gap-2 mb-8">
              <input
                name="title"
                placeholder="Add a todo..."
                required
                className="flex-1 rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm text-zinc-100 placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-600"
              />
              <button type="submit" className="rounded-md bg-zinc-100 px-4 py-2 text-sm font-medium text-zinc-900 hover:bg-zinc-300 transition-colors">
                Add
              </button>
            </form>
    
            <ul className="space-y-2">
              {todos.map((todo) => (
                <li key={todo.id} className="flex items-center gap-3 rounded-md border border-zinc-800 bg-zinc-900 px-3 py-2">
                  <form action={toggleTodo}>
                    <input type="hidden" name="id" value={todo.id} />
                    <button type="submit" className="text-lg leading-none">{todo.completed ? "✅" : "⬜"}</button>
                  </form>
                  <span className={`flex-1 text-sm ${todo.completed ? "line-through text-zinc-500" : "text-zinc-100"}`}>{todo.title}</span>
                  <form action={deleteTodo}>
                    <input type="hidden" name="id" value={todo.id} />
                    <button type="submit" className="text-sm text-red-400 hover:text-red-300 transition-colors">✕</button>
                  </form>
                </li>
              ))}
            </ul>
    
            {todos.length === 0 && <p className="text-center text-sm text-zinc-500 mt-4">No todos yet.</p>}
          </main>
        </div>
      );
    }
    
  5. 在本地运行应用程序。

    npm run dev
    
  6. 在浏览器中打开 http://localhost:3000 ,验证应用程序是否正在运行并连接到本地数据库。

    Web 浏览器中运行的新应用程序的屏幕截图,其中多个项保存到数据库。

    小窍门

    可以通过连接 Visual Studio Code DocumentDB 扩展到 DocumentDB 容器来查看此应用程序创建的数据。 数据库已命名 work-management ,并且集合已命名 tasks

  7. 停止应用程序。

创建 Azure DocumentDB 群集

创建 Azure DocumentDB 群集以在云中托管应用程序数据。

  1. 为群集名称和目标资源组名称创建变量。

    RESOURCE_GROUP_NAME="<resource-group-name>"
    CLUSTER_NAME="<cluster-name>"
    ADMIN_USERNAME="<admin-username>"
    ADMIN_PASSWORD="<admin-password>"
    

    Important

    将占位符中的值替换为您自己的值。 群集名称必须多区域唯一。

  2. 创建 Azure DocumentDB 群集。

    az cosmosdb mongocluster create \
      --resource-group $RESOURCE_GROUP_NAME \
      --cluster-name $CLUSTER_NAME \
      --location "chinanorth3" \
      --administrator-login $ADMIN_USERNAME \
      --administrator-login-password $ADMIN_PASSWORD \
      --server-version "8.0" \
      --shard-node-tier "M30" \
      --shard-node-ha false \
      --shard-node-disk-size-gb 128 \
      --shard-node-count 1
    
  3. 创建防火墙规则以允许从当前 IP 地址进行访问。

    MY_IP=$(curl -s ifconfig.me)
    
    az cosmosdb mongocluster firewall rule create \
      --resource-group $RESOURCE_GROUP_NAME \
      --cluster-name $CLUSTER_NAME \
      --rule-name "allow-my-ip" \
      --start-ip-address $MY_IP \
      --end-ip-address $MY_IP
    
  4. 获取群集的连接字符串。

    az cosmosdb mongocluster show \
      --resource-group $RESOURCE_GROUP_NAME \
      --cluster-name $CLUSTER_NAME \
      --query "connectionString" \
      --output tsv
    
  5. 记录连接字符串的值。 你在下一步中使用它。

将应用程序连接到 Azure DocumentDB

更新应用程序以连接到 Azure DocumentDB 而不是本地容器。

  1. app/data.tsx 中,将本地连接字符串替换为 Azure DocumentDB 连接字符串。

    const connectionString = "<azure-documentdb-connection-string>";
    

    Important

    <azure-documentdb-connection-string> 替换为在上一步中记录的连接字符串。 门户中提供的连接字符串不包括密码。 将连接字符串中的 <password> 占位符替换为创建群集时指定的密码。

  2. 再次启动应用程序。

    npm run dev
    
  3. 在浏览器中打开 http://localhost:3000 。 应用程序现在将数据读取并写入 Azure DocumentDB 群集。

清理资源

如果不再需要在本教程中创建的资源,请将其删除以避免产生费用。

  1. 删除 Azure DocumentDB 群集。

    az cosmosdb mongocluster delete \
      --resource-group $RESOURCE_GROUP_NAME \
      --cluster-name $CLUSTER_NAME \
      --yes
    
  2. 停止并删除 DocumentDB 容器。

    docker stop documentdb
    docker rm documentdb
    

后续步骤