deno.com
在本页

API 服务器与 Postgres

Postgres 是一个流行的 Web 应用程序数据库,因为它具有灵活性和易用性。本指南将向您展示如何将 Deno Deploy 与 Postgres 一起使用。

概述 跳转到标题

我们将为一个简单的待办事项列表应用程序构建 API。它将有两个端点

GET /todos 将返回所有待办事项的列表,而 POST /todos 将创建一个新的待办事项。

GET /todos
---
title: "returns a list of all todos"
---
[
  {
    "id": 1,
    "title": "Buy bread"
  },
  {
    "id": 2,
    "title": "Buy rice"
  },
  {
    "id": 3,
    "title": "Buy spices"
  }
]

POST /todos
---
title: "creates a new todo"
---
"Buy milk"
---
title: "returns a 201 status code"
---

在本教程中,我们将

设置 Postgres 跳转到标题

本教程将完全侧重于连接到未加密的 Postgres。如果您想使用自定义 CA 证书进行加密,请使用此处的文档。

要开始使用,我们需要创建一个新的 Postgres 实例供我们连接。在本教程中,您可以使用 Neon PostgresSupabase,因为它们都提供免费的托管 Postgres 实例。如果您想将数据库托管在其他地方,也可以这样做。

Neon Postgres 跳转到标题

  1. 访问 https://neon.tech/ 并单击注册以使用电子邮件、Github、Google 或合作伙伴帐户注册。注册后,您将被定向到 Neon 控制台以创建您的第一个项目。

  2. 输入您的项目名称,选择 Postgres 版本,提供数据库名称,然后选择一个区域。通常,您需要选择离您的应用程序最近的区域。完成后,单击创建项目

  3. 您将看到新项目的连接字符串,您可以使用该字符串连接到数据库。保存连接字符串,它看起来像这样

    postgres://alex:[email protected]/dbname?sslmode=require
    

Supabase 跳转到标题

  1. 访问 https://app.supabase.io/ 并单击“New project(新项目)”。
  2. 为您的数据库选择名称、密码和区域。请务必保存密码,因为您稍后会需要它。
  3. 单击“Create new project(创建新项目)”。创建项目可能需要一段时间,请耐心等待。
  4. 项目创建完成后,导航到左侧的“Database(数据库)”选项卡。
  5. 转到“Connection Pooling(连接池)”设置,然后从“Connection String(连接字符串)”字段复制连接字符串。这是您将用于连接到数据库的连接字符串。将您之前保存的密码插入到此字符串中,然后将字符串保存在某处 - 您稍后将需要它。

编写和部署应用程序 跳转到标题

我们现在可以开始编写我们的应用程序了。首先,我们将在控制面板中创建一个新的 Deno Deploy playground:在 https://dash.deno.com/projects 上按“New Playground(新建 Playground)”按钮。

这将打开 playground 编辑器。在我们真正开始编写代码之前,我们需要将 Postgres 连接字符串放入环境变量中。为此,请单击编辑器左上角的项目名称。这将打开项目设置。

从这里,您可以通过左侧导航菜单导航到“Settings(设置)”->“Environment Variable(环境变量)”选项卡。在“Key(键)”字段中输入“DATABASE_URL”,并将您的连接字符串粘贴到“Value(值)”字段中。现在,按“Add(添加)”。您的环境变量现已设置。

让我们返回编辑器:为此,通过左侧导航菜单转到“Overview(概述)”选项卡,然后按“Open Playground(打开 Playground)”。让我们从使用 Deno.serve() 提供 HTTP 请求开始

Deno.serve(async (req) => {
  return new Response("Not Found", { status: 404 });
});

您已经可以使用 Ctrl+S (或 Mac 上的 Cmd+S )保存此代码。您应该看到右侧的预览页面自动刷新:现在显示“Not Found(未找到)”。

接下来,让我们导入 Postgres 模块,从环境变量中读取连接字符串,并创建一个连接池。

import * as postgres from "https://deno.land/x/[email protected]/mod.ts";

// Get the connection string from the environment variable "DATABASE_URL"
const databaseUrl = Deno.env.get("DATABASE_URL")!;

// Create a database pool with three connections that are lazily established
const pool = new postgres.Pool(databaseUrl, 3, true);

同样,您现在可以保存此代码,但这次您应该看不到任何更改。我们正在创建一个连接池,但我们实际上尚未对数据库运行任何查询。在我们这样做之前,我们需要设置我们的表架构。

我们想要存储一个待办事项列表。让我们创建一个名为 todos 的表,其中包含一个自增 id 列和一个 title

const pool = new postgres.Pool(databaseUrl, 3, true);

// Connect to the database
const connection = await pool.connect();
try {
  // Create the table
  await connection.queryObject`
    CREATE TABLE IF NOT EXISTS todos (
      id SERIAL PRIMARY KEY,
      title TEXT NOT NULL
    )
  `;
} finally {
  // Release the connection back into the pool
  connection.release();
}

现在我们有了一个表,我们可以为 GET 和 POST 端点添加 HTTP 处理程序。

Deno.serve(async (req) => {
  // Parse the URL and check that the requested endpoint is /todos. If it is
  // not, return a 404 response.
  const url = new URL(req.url);
  if (url.pathname !== "/todos") {
    return new Response("Not Found", { status: 404 });
  }

  // Grab a connection from the database pool
  const connection = await pool.connect();

  try {
    switch (req.method) {
      case "GET": { // This is a GET request. Return a list of all todos.
        // Run the query
        const result = await connection.queryObject`
          SELECT * FROM todos
        `;

        // Encode the result as JSON
        const body = JSON.stringify(result.rows, null, 2);

        // Return the result as JSON
        return new Response(body, {
          headers: { "content-type": "application/json" },
        });
      }
      case "POST": { // This is a POST request. Create a new todo.
        // Parse the request body as JSON. If the request body fails to parse,
        // is not a string, or is longer than 256 chars, return a 400 response.
        const title = await req.json().catch(() => null);
        if (typeof title !== "string" || title.length > 256) {
          return new Response("Bad Request", { status: 400 });
        }

        // Insert the new todo into the database
        await connection.queryObject`
          INSERT INTO todos (title) VALUES (${title})
        `;

        // Return a 201 Created response
        return new Response("", { status: 201 });
      }
      default: // If this is neither a POST, or a GET return a 405 response.
        return new Response("Method Not Allowed", { status: 405 });
    }
  } catch (err) {
    console.error(err);
    // If an error occurs, return a 500 response
    return new Response(`Internal Server Error\n\n${err.message}`, {
      status: 500,
    });
  } finally {
    // Release the connection back into the pool
    connection.release();
  }
});

就这样 - 应用程序完成了。通过保存编辑器来部署此代码。您现在可以 POST 到 /todos 端点以创建一个新的待办事项,并且可以通过向 /todos 发出 GET 请求来获取所有待办事项的列表

$ curl -X GET https://tutorial-postgres.deno.dev/todos
[]⏎

$ curl -X POST -d '"Buy milk"' https://tutorial-postgres.deno.dev/todos

$ curl -X GET https://tutorial-postgres.deno.dev/todos
[
  {
    "id": 1,
    "title": "Buy milk"
  }
]

一切正常 🎉

本教程的完整代码

作为额外的挑战,尝试添加一个 DELETE /todos/:id 端点来删除待办事项。URLPattern API 可以对此有所帮助。

您找到所需内容了吗?

隐私政策