【Node.js】如何使用 Express.js 和 TypeScript 构建 CRUD API

如何使用 Express.js 和 TypeScript 构建 CRUD API
作者:Racheal Kuranchie
来源:https://medium.com/@rachealkuranchie/how-to-build-a-crud-api-with-express-js-and-typescript-21c7c66e5296


文章目录

      • 如何使用 Express.js 和 TypeScript 构建 CRUD API
        • 先决条件
        • 项目设置
        • 应用程序的包
        • 文件夹结构
        • 服务器连接
        • 创建模式和模型
        • 创建服务
        • 创建控制器
        • 创建路由
        • 测试
        • 结论

如何使用 Express.js 和 TypeScript 构建 CRUD API

在当今的数字世界中,API 是现代 Web 应用程序的支柱,它允许客户端和服务器端组件之间无缝通信。在构建 API 的最流行框架中,Express.js 以其简单性、灵活性和可扩展性脱颖而出。但是,为什么要满足于一个基本的 Express.js API,当你可以用 TypeScript 的力量来增强它呢?在本教程中,我们将向您展示如何使用 Express.js 和 TypeScript 创建一个完整的 CRUD(创建、读取、更新、删除)API,包括数据验证和数据库集成。所以系好安全带,准备将您的 API 开发技能提升到下一个水平!
Mongoose 将作为我们执行逻辑的对象文档映射器(ODM),MongoDB 将被用作我们的数据库,Joi 用于数据验证。

先决条件
  1. Node.js
    在我们开始之前,请确保您的机器上安装了 Node。

  2. MongoDB
    同样,请确保您有一个 MongoDB 实例,无论是来自 MongoDB Atlas 平台,还是来自本地安装。

    • 访问 MongoDB Atlas 平台,注册并创建一个集群,这将生成一个连接字符串,用于本应用程序。点击这里 指导您完成设置您的 mongo atlas 的过程。
    • 本地安装的 MongoDB 应该给您留下像这样的连接字符串:“mongodb://localhost:27020/”。
项目设置

通过创建一个新文件夹或目录来启动项目,使用这个命令一步步进行。

mkdir api # 创建一个文件夹
cd api   # 切换到文件夹
npm init -y  # 初始化应用程序

这将创建 package.json 文件,它将是你所有包的中心。

// package.json
{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

初始化 package.json 后,接下来是安装本应用程序所需的包。

应用程序的包
  • Express.js (文档)
  • mongoose (文档)
  • mongodb (官网)
  • dotenv (文档)
  • typescript (官网)
  • nodemon (官网)
  • joi (文档)
npm install express dotenv mongoose mongodb joi
npm install nodemon --save-dev

一旦您安装了这些,您将看到 node_modules 和一个 package-lock.json 文件添加到 package.json 文件中。下一步是将 typescript 作为开发依赖项安装,以及为已安装的包安装类型。

npm install typescript ts-node @types/node @types/express --save-dev

您会注意到与 typescript 相关的依赖项被安装为 devDependencies。这是因为,尽管代码是用 Typescript 编写的,但它将被编译为“普通”的 JavaScript。

现在要在您的应用程序中使用 typescript,您需要初始化它。为此,我们将创建 tsconfig.json 文件,我们可以在其中自定义编译器选项。我们可以通过使用此命令来获取此文件。

npx tsc --init

tsconfig.json 文件包含许多编译器选项,其中大部分已被注释掉。尽管如此,它可以被定制以适应您的应用程序的特定需求。配置中使用的某些选项非常重要,包括:

  • target: 允许我们指定编译器将输出的目标 JavaScript 版本。
  • module: 指定在 JavaScript 代码中生成的模块代码。CommonJS 被支持,并且是 Node.js 的标准。
  • strict: 启用严格类型检查选项的选项。
  • esModuleInterop: 允许我们将 ES6 模块编译为 CommonJS 模块。
  • skipLibCheck: 如果设置为 true,则跳过默认库声明文件的类型检查。
  • forceConsistentCasingInFileNames: 当设置为 true 时,启用区分大小写的文件命名。

您将必须启用的一个选项称为 outDir,它指定编译步骤后输出将位于何处。注意,这是必要的,因为 typescript 代码需要被编译为普通 JavaScript。您可以在 tsconfig.json 文件的 /Emit / 部分找到此选项并取消注释它。

默认情况下,此选项的值设置为根目录。将其更改为 dist 文件夹:

文件夹结构

是时候创建您的文件夹结构了。我喜欢通过尽可能减少我的代码来实现关注点的分离。这有助于轻松调试,也使您的代码易于阅读和清洁。

创建一个 src 和 dist 文件夹。dist 文件夹将包含所有已编译的代码。在 src 文件夹中,创建这些文件夹:Controllers、Models、Routes、Services、Server 和 Config。在这些文件夹中,分别创建以下文件:post.controller.ts、posts.ts、post.routes.ts、post.service.ts、app.ts 和 db.config.ts。

在根目录中创建一个 .env 文件,并保留您的 mongo DB atlas 数据库用户名和密码。

安装并初始化应用程序中的 typescript 后,下一步是编辑 package.json 文件。将以下内容添加到脚本中:start、dev 和 build。

// package.json
{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon ./src/Server/app.ts",
    "dev": "nodemon ./src/Server/app.ts",
    "build": "tsc --project tsconfig.json"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "joi": "^17.9.1",
    "mongodb": "^4.13.0",
    "mongoose": "^6.8.2"
  },
  "devDependencies": {
    "@types/express": "^4.17.15",
    "@types/node": "^18.11.18",
    "nodemon": "^2.0.20",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.4"
  }
}

接下来的阶段是将您的数据库连接到您的应用程序。在 config 文件夹中的 db.config.ts 中,编写此代码。使用 mongoose,我们可以使用 mongoose.connect() 方法连接到我们的数据库并与之通信。

// db.config.ts
// 导入模块
import dotenv from 'dotenv'
import mongoose from 'mongoose'

dotenv.config()

// 来自 env 的详细信息
const username = process.env.username
const password = process.env.password
const dbName = 'Post'

// 连接字符串到 mongo atlas
const connectionString = `mongodb+srv://${username}:${password}@cluster0.tjh8e.mongodb.net/${dbName}?retryWrites=true&w=majority`

const options = {
  autoIndex: false, // 不构建索引
  maxPoolSize: 10, // 维护最多 10 个套接字连接
  serverSelectionTimeoutMS: 5000, // 尝试发送操作 5 秒
  socketTimeoutMS: 45000, // 45 秒后关闭套接字
  family: 4 // 使用 IPv4,跳过尝试 IPv6
};

// 数据库连接
export const db = mongoose.connect(connectionString, options)
.then(res => {
  if (res) {
    console.log(`Database connection successfully to ${dbName}`)
  }
})
.catch(err => {
  console.log(err)
})
服务器连接

现在我们已经连接了我们的数据库,下一件事是创建我们的服务器,并查看数据库是否真的已经连接。在 express.js 中创建服务器是直接的。您必须导入 express 并将其初始化,通过将其分配给一个变量,然后您可以访问所有 HTTP 方法来使用。为了在请求中传递 JSON 正文,您必须使用 express 方法,如 express.json()。

// app.ts
import express from 'express'
import { db } from '../Config/db.config'
import { router } from '../Routes/posts.routes'

const app = express()

// 中间件
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// 路由
app.use('/api/v1/posts', router)

db.then(() => {
  // 数据库连接成功后再启动服务器
  app.listen(7070, () => console.log('Server is listening on port 7070'))
});
创建模式和模型

进入模型文件夹中的 posts.ts,并使用以下代码创建您的模式和模型。模式是数据库表的表示,它描述了您数据库中的类型,而模型是一个设计,它决定了应该在模式中包含什么。PostschemaValidate 指定了请求体中所需的字段类型,以便保存。它作为请求的另一层验证。

// posts.ts

// 导入模块
import { Schema, model } from 'mongoose'
import Joi from 'joi'

// 验证模式
export const PostschemaValidate = Joi.object({
  title: Joi.string().required(),
  description: Joi.string().required(),
  author: Joi.string().required(),
  published: Joi.boolean().required(),
})

// 创建接口
interface IPosts {
  title: string,
  description: string,
  author: string,
  published: boolean,
}

// Posts模式
const postSchema = new Schema({
  title: {
    type: String,
    // ... 其他 schema 选项
  },
  // ... 其他字段
})

// 创建模型
export const Post = model('Post', postSchema)
创建服务

创建模型后,下一步是创建服务类,它将包含我们想要执行的所有操作。在服务类中,我们将创建不同的函数,使用 mongoose 查询如 create, find, findById 等来读取、创建、更新和删除帖子。

// post.service.ts

// 导入模块
import { Post } from '../Models/posts'

export class postService {
  // 创建帖子
  async createPost(data: any) {
    try {
      const newPost = await Post.create(data)
      return newPost
    } catch (error) {
      // 处理错误
    }
  }

  // 其他 CRUD 操作的函数...
}

// 导出类
export const postServices = new postService()
创建控制器

现在,在控制器中,我们将调用服务中的每个函数并执行它。为了使我们的代码易于阅读和清洁,我们尽量保持每个文件尽可能简单,并且每个函数在应用程序中只有一个责任。代码也很容易维护和扩展,因为我们已经分离了关注点,所以我们可以专注于每个功能。

// post.controller.ts

// 导入模块
import { postServices } from '../Services/post.service'
import { Request, Response } from 'express'
import { PostschemaValidate } from '../Models/posts'

class postController {
  // 添加帖子控制器
  addpost = async (req: Request, res: Response) => {
    // 要保存在数据库中的数据
    const data = {
      title: req.body.title,
      author: req.body.author,
      description: req.body.description,
      published: req.body.published,
    }
    // 验证请求
    const { error, value } = PostschemaValidate.validate(data)
    if (error) {
      // 处理验证错误
      return res.status(400).send({ message: 'Validation error', error })
    }
    const post = await postServices.createPost(value)
    res.send(post)
  }

  // 其他 CRUD 控制器的方法...
}

// 导出类
export const PostController = new postController()
创建路由

路由是我们将使用 HTTP(POST, GET, PUT, DELETE)等方法在控制器中执行操作的地方。我们将根据每个函数创建我们的端点。

// post.routes.ts

// 导入模块
import express from 'express'
import { PostController } from '../Controllers/post.controller'

// 初始化路由器
export const router = express.Router()

// 添加帖子路由
router.post('/', PostController.addpost)

// 获取帖子
router.get('/', PostController.getPosts)

// 获取单个帖子
router.get('/:id', PostController.getAPost)

// 更新帖子
router.put('/:id', PostController.updatePost)

// 删除帖子
router.delete('/:id', PostController.deletePost)
测试

在 Postman 或 REST 客户端上测试您的端点。

curl -X POST --data '{
  "title": "My Post",
  "description": "my post description",
  "author": "aichaoxy",
  "published": false
}' http://localhost:7070/api/v1/posts

curl -X GET http://localhost:7070/api/v1/posts

curl -X PUT --data '{
  "title": "My Javascript Post"
}' http://localhost:7070/api/v1/posts/6421b0e73169ceabf11644e9

curl -X DELETE http://localhost:7070/api/v1/posts/6421b0e73169ceabf11644e9

最后,在 mongo atlas 中检查您的 mongo 数据库中的数据。

结论

您已经创建了一个用于创建、读取、更新和从数据库中删除数据的 API。您可以查看我之前 关于 认证和授权 的帖子 。


这篇文章提供了一个完整的教程,从项目设置到测试,展示了如何使用 Express.js 和 TypeScript 创建一个 CRUD API。它包括了设置环境、安装依赖项、创建数据库连接、定义模式和模型、编写服务和控制器逻辑,以及设置路由。最后,作者还提供了如何测试 API 端点的示例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/597584.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

知识图谱融入RAG模型:LinkedIn重塑智能客服新范式【附LeCun哈佛演讲PPT】

原文:Retrieval-Augmented Generation with Knowledge Graphs for Customer Service Question Answering 一、研究背景与问题 在客服领域,快速准确地匹配用户问题与历史工单,是提供优质回答的关键。传统的检索增强生成(Retrieval-Augmented Generation, RAG)方法虽…

IDEA-控制台日志过滤插件 - Grep Console

IDEA-控制台日志过滤插件 - Grep Console 当idea控制台日志较多时,为了方便查找关键字,使用Grep Console插件,指定控制台中关键字高亮显示 1.安装 2.使用 2.1 高亮显示 控制台中指定颜色高亮显示指定字符 效果: 重启项目后还是会高亮显示 取…

【软考高项】三十三、质量管理

一、管理基础 质量定义 国际标准:反映实体满足主体明确和隐含需求的能力的特性总和。 国家标准:一组固有特性满足要求的程度。固有特性是指在某事或某物中本来就有的,尤其是那种永久的可区分的特征。 ➢ 对产品来说,例如…

缓存菜品操作

一:问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大。 二:实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: 每个分…

k8s保持pod健康

存活探针 Kubemetes 可以通过存活探针 (liveness probe) 检查容器是否还在运行。可以为 pod 中的每个容器单独指定存活探针。如果探测失败,Kubemetes 将定期执行探针并重新启动容器。 Kubemetes 有以下三种探测容器的机制: HTTP GET 探针对容器的 IP 地…

Day61:单调栈 739. 每日温度 496.下一个更大元素 I

739. 每日温度 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。 示例 1: 输…

发表博客之:gemm/threadblock/threadblock_swizzle.h 文件夹讲解,cutlass深入讲解

文章目录 [发表博客之:gemm/threadblock/threadblock_swizzle.h 文件夹讲解,cutlass深入讲解](https://cyj666.blog.csdn.net/article/details/138514145)先来看一下最简单的struct GemmIdentityThreadblockSwizzle结构体 发表博客之:gemm/th…

vue2 webpack-dev-server Unknown promise rejection reason

在vue.config.js中添加如下配置,重启项目即可 module.exports defineConfig({devServer: {client: {overlay: false,},} })参考

探索中位数快速排序算法:高效寻找数据集的中间值

在计算机科学领域,寻找数据集的中位数是一个常见而重要的问题。而快速排序算法作为一种高效的排序算法,可以被巧妙地利用来解决中位数查找的问题。本文将深入探讨中位数快速排序算法的原理、实现方法以及应用场景,带你领略这一寻找中间值的高…

vue 金额组件,输入提示单位:‘千’、‘万’、‘十万’...并用‘,’三个格式化

近期项目中遇到一个需求,金额输入框,输入过程中自动提示‘千’、‘万’、‘十万’、‘百万’......等单位提示,鼠标失去焦点后,并用‘,’三位隔开计数。 例如: 输入:12345.99 失去焦点:12,34…

Vue--》从零开始打造交互体验一流的电商平台(一)

今天开始使用 vue3 ts 搭建一个电商项目平台,因为文章会将项目的每处代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的github上,大家可以自行去进行下载运行,希…

【Node.js工程师养成计划】之express中间件与接口规范

一、Express中间件的概念与基本应用 const express require(express)// 加一个注释,用以说明,本项目代码可以任意定制更改 const app express()const PORT process.env.PORT || 3000// // 挂载路由 // app.use(/api, router)// // 挂载统一处理服务端…

【倪亲斫经典水墨云纹仲尼式】倪诗韵亲斫古琴

【倪亲斫经典水墨云纹仲尼式】倪诗韵亲斫古琴 松透润,适合大曲文曲潇湘欸乃平沙,余韵悠长,手感极其舒适,久弹不疲。

[Linux][网络][TCP][三][超时重传][快速重传][SACK][D-SACK][滑动窗口]详细讲解

目录 1.超时重传1.什么是超时重传?2.超时时间是如何确定的? 2.快速重传3.SACK4.D-SACK1.ACK丢失2.网络延迟 5.滑动窗口0.问题抛出1.发送方的滑动窗口2.如何表示发送方的四个部分?3.接收方的滑动窗口4.滑动窗口的完善理解 1.超时重传 1.什么是…

C++手写协程项目(协程实现线程结构体、线程调度器定义,线程挂起函数、线程切换函数、线程恢复函数、线程结束函数、线程结束判断函数,模块测试)

协程结构体定义 之前我们使用linux下协程函数实现了线程切换,使用的是ucontext_t结构体,和基于这个结构体的四个函数。现在我们要用这些工具来实现我们自己的一个线程结构体,并实现线程调度和线程切换、挂起。 首先我们来实现以下线程结构体…

Splay 树简介

【Splay 树简介】 ● Treap 树解决平衡的办法是给每个结点加上一个随机的优先级,实现概率上的平衡。Splay 树直接用旋转调整树的形态,通过旋转改善树的平衡性。计算量小,效果好。 ● Splay 树的旋转主要分为“单旋”和“双旋”。 所谓“单旋”…

基于52单片机的AS608指纹密码锁电路原理图+源程序+PCB实物制作

目录 1、前言 2、实物图 3、PCB图 4、原理图 5、程序 资料下载地址:基于52单片机的AS608指纹密码锁电路原理图源程序PCB实物制作 1、前言 这是一个基于AS608STC89C52单片机的指纹识别和键盘密码锁。 里面包括程序,原理图,pcb图和实…

OpenNJet:云原生技术中的创新者与实践者

目录 引言OpenNJet介绍OpenNJet优势1. 性能无损动态配置2. 灵活的CoPilot框架3. 支持HTTP/34. 支持国密5. 企业级应用6. 高效安全 OpenNJet 编译与安装环境准备编译环境配置配置yum源yum 安装软件包创建符号连接修改 ld.so.conf 配置 编译代码 部署 WEB SERVER配置OpenNJet部署…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-13-按键实验

前言: 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

FTP协议与工作原理

一、FTP协议 FTP(FileTransferProtocol)文件传输协议:用于Internet上的控制文件的双向传输,是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同…
最新文章