使用 PostgreSQL 开发 CRUD Node.js 应用程序

2025-06-08

使用 PostgreSQL 开发 CRUD Node.js 应用程序

各位程序员们好!你们好吗?今天我们将开启一系列文章,学习如何使用 Node.js 和 PostgreSQL 开发一个 CRUD 应用程序,重点关注后端。之后,我们将使用Azure Functions将项目迁移到无服务器架构,将本地数据库迁移到Azure PostgreSQL,最后使用GitHub Actions部署此应用程序。

我在 GitHub 上创建了一个存储库(此处),其中分配了该项目的所有源代码以及我将在 5 篇文章中提到的资源。

更新的存储库在这里

重要提示:本项目将重点关注后端。如果在项目结束时,我们想要测试前端某个应用程序中创建的后端 API,那么我可以创建一个通用的前端,专门用于测试创建的 API!

好了,说了这么多,走吧?!

目录

教程中使用的资源

我将在本文中提到一些我们将在开发此应用程序过程中使用的资源。它们如下:

吸引众多开发者使用 PostgreSQL 的原因之一是它完全免费,可在任何操作系统上运行,而且最重要的是:它完全开源!Uber、Netflix、Spotify、Instagram、Reddit 等众多大型公司都在使用 PostgreSQL。这就是 PostgreSQL 如此受欢迎的原因!

PostgreSQL 是我学习使用的第一个数据库,2012 年我在大学学习数据库 I 和 II 课程时就接触过它。我一直很喜欢 PostgreSQL,因为它非常简单易用!

我们需要在机器上安装 PostgreSQL。但也可以使用一些 Docker 镜像来继续本教程。

安装 PostgreSQL 数据库

好吧,我将在这里教您如何为不同的操作系统用户安装 PostgreSQL:

  • Windows:对于 Windows 用户,请下载适用于 Windows 的 Postgres。安装过程简单易懂,就像在 Windows 上安装程序一样。

  • macOS :对于 Mac 用户,只需在此处下载软件包。此外,您还需要安装Homebrew。如果您在安装过程中遇到问题或困难,建议您观看此处的视频

  • Linux:对于 Linux 用户,由于 Linux 有无数不同的版本,我建议您查看此处的PostgreSQL 指南

就我而言,我将使用 Windows,因为它是我的主要开发机器。我将使用 PostgreSQL 12 版本。安装完成后,只需搜索 pgAdmin。浏览器页面将打开:http://127.0.0.1: 16450/browser/,现在我们就可以开始使用了!

屏幕截图-03-01-20-晚上10点01分.png

在 PostgreSQL 中创建表

我们现在将创建一个表,该表具有将用于持久保存在后端使用的属性。

该课程将是:Product




Class: Product

- productId: integer primary
- product_name_: varchar
- quantity: int
- price: real



Enter fullscreen mode Exit fullscreen mode

现在打开 PgAdmin。您可能需要输入密码才能在 PgAdmin 中执行某些操作。您可能需要创建一个数据库。只需使用您想要的名称即可。创建数据库后,右键单击“创建脚本”,然后在 PostgreSQL 上运行以下脚本(如下图所示):




CREATE TABLE products (
    productId SERIAL PRIMARY KEY,
    productName VARCHAR(255) NOT NULL,
    quantity INTEGER NOT NULL,
    price NUMERIC(5,2)
);



Enter fullscreen mode Exit fullscreen mode

postgresql-01.gif

现在,您要做的就是访问新创建的表!

postgresql-02.gif

太棒了!我们已经创建好了表格!

在 Node.js 中创建应用程序架构

现在我们的表已经创建好了,让我们用 Node.js 创建项目。在这个项目中,我将遵循 SOLID 和 Clean Code 原则。如果您想了解更多关于这两个主题的信息,我强烈建议您查看以下两个链接:

好了,让我们开始构建我们的项目。创建一个名为 API 的文件夹并运行以下命令:



> npm init -y


Enter fullscreen mode Exit fullscreen mode

此命令会创建一个标准的 package.json 文件。现在我们将安装以下软件包:




> npm i --save-dev husky nodemon



Enter fullscreen mode Exit fullscreen mode

并且还安装其他包作为依赖项:




> npm i cors dotenv express express-promise-router pg



Enter fullscreen mode Exit fullscreen mode

最后,package.json文件将如下所示:



{
  "name": "crud-nodejs-psql",
  "version": "1.0.0",
  "description": "Aplicação CRUD com Node.js & PostgreSQL",
  "main": "server.js",
  "scripts": {
    "dev": "nodemon",
    "lint": "eslint --ext .js,.html -f ./node_modules/eslint-friendly-formatter . --fix",
    "prepush": "npm run lint",
    "start": "node server.js"
  },
  "keywords": [
    "node.js",
    "javascript",
    "postgresel",
    "azure",
    "serverless",
    "azure-functions",
    "azure-devops",
    "azure-storage",
    "github-actions",
    "app-service",
    "express"
  ],
  "author": "Glaucia Lemos",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/glaucia86/nodejs-postgresql-azure/issues"
  },
  "homepage": "https://github.com/glaucia86/nodejs-postgresql-azure#readme",
  "devDependencies": {
    "eslint": "^6.8.0",
    "eslint-config-airbnb-base": "^14.0.0",
    "eslint-plugin-import": "^2.20.1",
    "husky": "^4.2.3",
    "nodemon": "^2.0.2"
  },
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "eslint-friendly-formatter": "^4.0.1",
    "eslint-plugin-html": "^6.0.0",
    "express": "^4.17.1",
    "express-promise-router": "^3.0.3",
    "pg": "^7.18.2"
  }
}


Enter fullscreen mode Exit fullscreen mode

创建文件夹和文件的结构,如下图所示:

屏幕截图-03-05-20-晚上10点50分.png

开发应用程序

之后我不会解释每个文件的作用。因为本文的重点是创建一个 RESTful API,最终必须将其持久化到 PostgreSQL 中。

打开Visual Studio Code,让我们开始开发文件:server.js 并包含以下代码块:

  • 文件:server.js


/**
 * File: server.js
 * Description: arquivo responsável por toda a configuração e execução da aplicação.
 * Data: 02/03/2020
 * Author: Glaucia Lemos
 */

const app = require('./src/app');

const port = process.env.PORT || 3000;

app.listen(port, () => {
  console.log('Aplicação executando na porta ', port);
});


Enter fullscreen mode Exit fullscreen mode

现在,打开src -> app.js文件并包含以下代码块:

  • 文件:app.js


const express = require('express');
const cors = require('cors');

const app = express();

// ==> Rotas da API:
const index = require('./routes/index');
// const productRoute = require('./routes/product.routes');

app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.json({ type: 'application/vnd.api+json' }));
app.use(cors());

app.use(index);
// app.use('/api/', productRoute);

module.exports = app;


Enter fullscreen mode Exit fullscreen mode

请注意,此处app.js file有几行代码被注释掉了,只是为了能够初步执行我们的 API 并检查开发是否正确。稍后我们将对此文件进行一些重大更改,然后取消注释这些行。

最后,打开src -> routes -> index.js文件并包含以下代码块:

  • 文件:src->routes->index.js


/**
 * File: src/routes/index.js
 * Description: arquivo responsável pela chamada da Api da aplicação.
 * Data: 02/03/2020
 * Author Glaucia Lemos
 */

const express = require('express');

const router = express.Router();

router.get('/api', (req, res) => {
  res.status(200).send({
    success: 'true',
    message: 'Seja bem-vindo(a) a API Node.js + PostgreSQL + Azure!',
    version: '1.0.0',
  });
});

module.exports = router;


Enter fullscreen mode Exit fullscreen mode

现在,打开 apifolder 内的命令提示符并运行以下命令:




> nodemon



Enter fullscreen mode Exit fullscreen mode

然后打开邮递员并在(GET)中包含以下URL localhost:3000/api/::

屏幕截图-03-02-20-上午12-47.png

如果屏幕显示的内容与上面类似,则表示我们的 API 正常运行!现在,让我们深入开发。开始吧!

进一步了解“node-postgres”包

你可能注意到,我们在安装一些软件包时,包含了node-postgres包。这个包对于我们在 Node.js 上使用 PostgreSQL 客户端至关重要。

这个包是一个开源项目。它有简单易懂的文档——教我们如何在 Promises 中或使用 Async/Await 来实现这个包。这对我编写这篇教程很有帮助!

我建议阅读包文档,可以在这里找到。

在这个项目中,我决定使用 node-postgres 而不是 Sequelize,后者是 PostgreSQL、MySQL、MariaDB、SQLite 和 Microsoft SQL Server 用户广泛使用的 ORM。只是为了让操作project-1更简单。

由于我们已经在一开始安装了 node-postgres 包,让我们继续吧!

使用“dotenv”创建环境变量

您可能注意到的另一点是,我们还安装了 dotenv 包。这个服务器包很重要,它可以存储我们不想在执行commit.

至于我们将如何使用数据库连接字符串,以及此连接字符串中是否存在敏感数据,我们不希望将其公开给所有人。我们现在将在项目中解决这个问题。为此,请按照以下步骤操作:




DATABASE_URL=postgres://{db_username}:{db_password}@{host}:{port}/{db_name}



Enter fullscreen mode Exit fullscreen mode

如果你不知道你的 PostgreSQL db_username 是什么,只需右键单击 PgAdmin 服务器,然后转到“属性”->“连接”,你就会找到用户名。参见下面的动图:

postgresql-03.gif

在文件“database.js”中配置到数据库的连接字符串

现在我们已经将我们的内容包含在.envconnectionstring文件中,现在是时候开始开发和配置我们的应用程序与 PostgreSQL 的数据库连接了。

为此,打开database.js文件并包含以下代码块:

  • 配置/数据库.js:


/**
 * Arquivo: config/database.js
 * Descrição: arquivo responsável pelas 'connectionStrings da aplicação: PostgreSQL.
 * Data: 04/03/2020
 * Author: Glaucia Lemos
 */

const { Pool } = require('pg');
const dotenv = require('dotenv');

dotenv.config();

// ==> Conexão com a Base de Dados:
const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});

pool.on('connect', () => {
  console.log('Base de Dados conectado com sucesso!');
});

module.exports = {
  query: (text, params) => pool.query(text, params),
};


Enter fullscreen mode Exit fullscreen mode

此块与我们的连接字符串相连,以便我们最终可以开始使用 Node.js 在 PostgreSQL 中保存数据。

请注意,我们正在使用node-postgres 包。如果您想进一步了解pg.Pool,我建议您阅读此处,因为从现在开始我们将经常使用它,包括此构建器的属性!

路线:(POST)'创建产品'

我们已经完成了应用程序的配置,并且正在连接到数据库!现在游戏即将开始!为此,我们将开发第一条路由。为此,我们将从现在开始大量使用两个文件:product.controller.jsproduct.routes.js

请按照以下步骤操作:

在product.routes.js文件中包含以下代码块

  • 文件:product.routes.js


// @ts-nocheck
/**
 * Arquivo: src/routes/product.routes.js
 * Descrição: arquivo responsável pelas rotas da api relacionado a classe 'Product'.
 * Data: 04/03/2020
 * Author Glaucia Lemos
 */

const router = require('express-promise-router')();
const productController = require('../controllers/product.controller');

// ==> Definindo as rotas do CRUD - 'Product':

// ==> Rota responsável por criar um novo 'Product': (POST): localhost:3000/api/products
router.post('/products', productController.createProduct);

module.exports = router;


Enter fullscreen mode Exit fullscreen mode

现在我们将开发文件中 createProduct 方法的逻辑product.controller.js

  • 控制器/product.controller.js


const db = require("../config/database");

// ==> Método responsável por criar um novo 'Product':

exports.createProduct = async (req, res) => {
  const { product_name, quantity, price } = req.body;
  const { rows } = await db.query(
    "INSERT INTO products (product_name, quantity, price) VALUES ($1, $2, $3)",
    [product_name, quantity, price]
  );

  res.status(201).send({
    message: "Product added successfully!",
    body: {
      product: { product_name, quantity, price }
    },
  });
};


Enter fullscreen mode Exit fullscreen mode

请注意,我们只需在代码中使用插入查询,就像在 SQL 脚本中一样。就是这样。当然,为了返回所有输入的值,我们会添加一条消息来确认已创建的产品并返回该产品的所有值。

现在,我们需要在测试应用程序之前更新app.js文件。为此,请取消注释product.routes所在行:

  • 文件:app.js


/**
 * Arquivo: app.js
 * Descrição: arquivo responsável por toda a configuração da aplicação.
 * Data: 02/03/2020
 * Author: Glaucia Lemos
 */

const express = require('express');
const cors = require('cors');

const app = express();

// ==> Rotas da API:
const index = require('./routes/index');
const productRoute = require('./routes/product.routes');

app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.json({ type: 'application/vnd.api+json' }));
app.use(cors());

app.use(index);
app.use('/api/', productRoute);

module.exports = app;


Enter fullscreen mode Exit fullscreen mode

打开命令提示符并在 api 文件夹中输入以下命令




> nodemon



Enter fullscreen mode Exit fullscreen mode
  1. 现在我们可以测试我们创建的第一个路由。现在,在以下端点打开 Postman: (POST) localhost: 3000/api/products,如下图所示:

postgresql-04.gif

如果出现如下信息:



{
    "message": "Product added successfully!",
    "body": {
        "product": {
            "product_name": "Logitech MK270 Wireless Keyboard and Mouse Combo",
            "quantity": "2",
            "price": "18.99"
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

这是因为它完美地持久化了。为了确认这一点,请打开已创建表的 PgAdmin,并按照下面的 gif 所示进行操作:

postgresql-05.gif

太棒了!一旦我们成功创建了第一条路线,剩下的就很容易了!

我们继续吧!

路线:(GET)'列出所有产品'

现在我们将创建一个路由,用于列出 PostgreSQL 中所有已创建并持久化的产品。由于 POST 路由已经创建,如果您能添加更多数据来帮助后续路由,那就更好了!

再次返回product.routes.js文件并添加列出所有产品的路由:

  • 文件:product.routes.js



// ==> Rota responsável por listar todos os 'Products': (GET): localhost:3000/api/products
router.get('/products', productController.listAllProducts);


Enter fullscreen mode Exit fullscreen mode

现在,回到文件product.controller.js并让我们开发listAllProducts方法的逻辑

  • 文件:product.controller.js



// ==> Método responsável por listar todos os 'Products':
exports.listAllProducts = async (req, res) => {
  const response = await db.query('SELECT * FROM products ORDER BY product_name ASC');
  res.status(200).send(response.rows);
};



Enter fullscreen mode Exit fullscreen mode

请注意,我这里执行的查询是:SELECT * FROM products ORDER BY product_name ASC。这里我要求按字母顺序返回所有持久化在 PostegreSQL 中的产品!我这样做是为了让结果看起来有点特别!;)

我们来测试一下,打开Postman看看结果:

postgresql-06.gif

完美运行!注意,如果我们需要使用子查询进行更复杂的 SELECT,按照这个逻辑,它也能完美运行!:)

路线:(GET)'按 ID 列出产品'

现在,这非常简单。只需将我们的 SQL 知识与我们在其他 Node.js 应用程序中已经创建的其他 CRUD 知识结合起来即可。

现在让我们创建路由,通过 ID 列出特定产品。再次打开文件 product.routes.js 并添加一条路由:

  • 文件:product.routes.js


(...)

// ==> Rota responsável por selecionar 'Product' pelo 'Id': (GET): localhost:3000/api/products/:id
router.get('/products/:id', productController.findProductById);

(...)


Enter fullscreen mode Exit fullscreen mode

现在打开文件product.controller.js,我们将开发此路由的逻辑:

  • 文件:product.controller.js


(...)

// ==> Método responsável por selecionar 'Product' pelo 'Id':
exports.findProductById = async (req, res) => {
  const productId = parseInt(req.params.id);
  const response = await db.query('SELECT * FROM products WHERE productid = $1', [productId]);
  res.status(200).send(response.rows);
}


Enter fullscreen mode Exit fullscreen mode

现在让我们在 Postman 上测试这个路由,看看会发生什么:

postgresql-07.gif

路线:(PUT)'按 ID 更新产品'

现在让我们回到product.routes.js文件来创建updateProductById路由,该路由将负责通过 Id 更新产品:

  • 文件:product.routes.js


(...)

// ==> Rota responsável por atualizar 'Product' pelo 'Id': (PUT): localhost: 3000/api/products/:id
router.put('/products/:id', productController.updateProductById);


Enter fullscreen mode Exit fullscreen mode

让我们回到updateProductById文件来开发product.controller.js方法的逻辑

  • 文件:product.controller.js


(...)

// ==> Método responsável por atualizar um 'Product' pelo 'Id':
exports.updateProductById = async (req, res) => {
  const productId = parseInt(req.params.id);
  const { product_name, quantity, price } = req.body;

  const response = await db.query(
    "UPDATE products SET product_name = $1, quantity = $2, price = $3 WHERE productId = $4",
    [product_name, quantity, price, productId]
  );

  res.status(200).send({ message: "Product Updated Successfully!" });
};


Enter fullscreen mode Exit fullscreen mode

更新完美!请看下面的动图:

postgresql-08.gif

现在让我们前往最后一条路线!

路线:(删除)'按 ID 删除产品'

终于,我们到了 api 的最后一条路由!让我们回到product.routes.js文件,并为deleteProductById方法创建路由

  • 文件:product.routes.js


(...)

// ==> Rota responsável por excluir 'Product' pelo 'Id': (DELETE): localhost:3000/api/products/:id
router.delete('/products/:id', productController.deleteProductById);

(...)


Enter fullscreen mode Exit fullscreen mode

最后,在文件product.controller.js中开发此路由的逻辑:

  • 文件:product.controller.js


(...)

// ==> Método responsável por excluir um 'Product' pelo 'Id':
exports.deleteProductById = async (req, res) => {
  const productId = parseInt(req.params.id);
  await db.query('DELETE FROM products WHERE productId = $1', [
    productId
  ]);

  res.status(200).send({ message: 'Product deleted successfully!', productId });
};


Enter fullscreen mode Exit fullscreen mode

一切都运行正常,如果我们打开 PostgreSQL,我们将看到现在我们只有 5 个注册产品!

postgresql-10.gif

结论

今天我们学习了如何使用 Node.js 创建 RESTFul 的 CRUD API,并将其本地持久化到 PostgreSQL 中。下一篇文章,我将教你如何在 Azure 应用服务中部署此应用程序!部署完成后,我们将在 Postman 上进行测试,之后再在 Swagger 上进行测试!

在这里,我想介绍一些有关 Node.js、Azure Database PostgreSQL 和 GitHub Actions 的优秀资源:

要了解其他最新消息,请务必在 Twitter 上关注我!

叽叽喳喳

并订阅我的Youtube 频道 - Glaucia Lemos观看每周有关 Web 开发、Node.js 和 JavaScript 的新视频!

屏幕截图-12-31-20-上午-01-06-.png

再见!😍

鏂囩珷鏉ユ簮锛�https://dev.to/glaucia86/developing-a-crud-node-js-application-with-postgresql-4c9o
PREV
您是否可以享受 Segredo para Aprender 或 Ser uma Ótima 或 Excelente Pessoa Desenvolvedora? “学习编码就像学习说一门新语言。” 〜格劳西娅·莱莫斯
NEXT
🛠️ 准备上线:我们推荐的 11 款顶级开源工具,助您开启事业!💻✨ Matomo(原名 Piwik)- matomo.org 人人可用的调度基础设施 Nextcloud 服务器 ☁ Metabase Gitea 目录 🌼 欢迎来到 Panora