欢迎光临散文网 会员登陆 & 注册

Node.js和TypeScript教程:使用Typescript、NodeJS和基于文件的存储系统构建REST API

2023-06-27 06:48 作者:晓枫Motivation  | 我要投稿

介绍

欢迎到我的博客!在本教程中,我将指导您完成使用 Node.js、Express 和 TypeScript 构建强大的微型电子商务 API 的过程。我们将共同探索各种功能和技术,使您能够为电子商务应用程序创建强大的 API。

我们在这个项目中的关键决策之一是实现基于文件的存储系统,而不是依赖 MongoDB 等传统数据库。这种方法简单且易于实施,非常适合小型应用程序或可能不需要成熟的数据库管理系统的场景。

本教程将涵盖用户管理、产品处理和身份验证等基本主题。

(更|多优质内|容:java567 点 c0m)


您将获得使用涵盖用户和产品数据的功能的实践经验,演示这些实体如何在电子商务 API 中进行交互。在本教程结束时,您将全面了解如何构建强大的 API,以实现与用户和产品资源的无缝交互。

因此,请与我一起踏上这个激动人心的旅程,我们将深入研究使用 Node.js、Express 和 TypeScript 创建微型电子商务 API。

在 Node.js 中开始使用 TypeScript 首先创建一个如下所示的项目目录。


接下来,使用以下命令创建具有默认设置的 package.json 文件,在项目目录中初始化 Node.js 项目:

 npm init -y

安装项目依赖项

您的 Node.js 项目需要几个依赖项才能使用 TypeScript 创建安全的 Express 服务器。像这样安装它们:

npm i express dotenv helmet cors http-status-codes uuid bcryptjs 要使用 TypeScript,您还需要安装稳定版本的 TypeScript 作为开发人员依赖项:

 npm i -D typescript

要有效地使用 TypeScript,您需要为之前安装的包安装类型定义:

 npm i -D @types/express @types/dotenv @types/helmet @types/cors @types/http-status-codes @types/uuid @types/bcryptjs


使用以下变量填充 .env 隐藏文件,该变量定义服务器可用于侦听请求的端口:

 PORT=7000


接下来,找到 src 文件夹根目录中的 app.js 文件,导入之前安装的项目依赖项,并使用 dotenv.config() 方法从本地 .env 文件加载任何环境变量:

 import express from "express"
 import * as dotevnv from "dotenv"
 import cors from "cors"
 import helmet from "helmet"
 
 dotevnv.config()
 
 if (!process.env.PORT) {
     console.log(`No port value specified...`)
 }
 
 const PORT = parseInt(process.env.PORT as string, 10)
 
 const app = express()
 
 app.use(express.json())
 app.use(express.urlencoded({extended : true}))
 app.use(cors())
 app.use(helmet())
 
 app.listen(PORT, () => {
     console.log(`Server is listening on port ${PORT}`)
 })


在此代码片段中,使用 Express 框架设置 Node.js 应用程序。以下是所发生情况的详细说明:

导入所需的模块:

Express被导入作为构建 Web 应用程序的主要框架。

导入dotenv来处理环境变量。

导入cors是为了实现跨源资源共享。

导入头盔是为了向 HTTP 响应添加安全标头。

该代码检查 PORT 环境变量是否已定义。如果没有,一条消息会记录到控制台。

使用 parseInt() 将 PORT 变量从字符串解析为整数。

Express 应用程序的实例是使用express() 创建的,并分配给app 变量。

Express应用程序中添加了中间件功能:

express.json()用于解析传入请求的 JSON 正文。

express.urlencoded({extended : true})用于解析传入请求的 URL 编码正文。

cors()用于启用跨源资源共享。

头盔()用于通过设置各种HTTP标头来增强应用程序的安全性。

Express 应用程序通过调用 app.listen() 开始侦听指定的端口。服务器运行后,一条指示端口号的消息将记录到控制台。

改进 TypeScript 开发工作流程

TypeScript 编译过程会增加应用程序的引导时间。但是,只要源代码发生更改,您就不需要重新编译整个项目。您可以设置 ts-node-dev 以显着减少进行更改时重新启动应用程序所需的时间。

首先安装此软件包以增强您的开发工作流程:

 npm i -D ts-node-dev

当任何所需文件发生更改时,ts-node-dev 会重新启动目标 Node.js 进程。但是,它在重新启动之间共享 Typescript 编译过程,这可以显着提高重新启动速度。

您可以在 package.json 中创建 dev npm 脚本来运行服务器。像这样更新你的 package.json 文件。

 {"name": "typescript-nodejs","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","dev": "ts-node-dev --pretty --respawn ./src/app.ts"},"keywords": [],"author": "","license": "ISC","dependencies": {"@types/nanoid": "^3.0.0","@types/uuid": "^9.0.2","bcryptjs": "^2.4.3","cors": "^2.8.5","dotenv": "^16.3.0","express": "^4.18.2","helmet": "^7.0.0","http-status-codes": "^2.2.0","nanoid": "^4.0.2","uuid": "^9.0.0"},"devDependencies": {"@types/bcryptjs": "^2.4.2","@types/cors": "^2.8.13","@types/dotenv": "^8.2.0","@types/express": "^4.17.17","@types/helmet": "^4.0.0","@types/http-status-codes": "^1.2.0","ts-node-dev": "^2.0.0"}}

让我们简单地分解一下 ts-node-dev 采用的选项:

--respawn:脚本退出后继续观察变化。

--pretty:使用漂亮的诊断格式化程序(TS_NODE_PRETTY)。

./src/app.ts:这是应用程序的入口文件。

现在,只需运行开发脚本即可启动您的项目:

 npm run dev

如果一切正常,您将看到一条消息,指示服务器正在侦听端口 7000 上的请求。

使用 TypeScript 接口对数据进行建模

在创建任何路由之前,定义要管理的数据的结构。我们的用户数据库将具有以下属性:

id:(字符串)项目记录的唯一标识符。 用户名:(字符串)项目的名称。 电子邮件:(数字)商品价格(以美分为单位)。 密码:(字符串)项目的描述。

使用以下定义填充 src/users/user.interface.ts:

 export interface User {
     username : string,
     email : string,
     password : string
 }
 
 export interface UnitUser extends User {
     id : string
 }
 
 export interface Users {
     [key : string] : UnitUser
 }


此代码定义了三个 TypeScript 接口:

  • 用户界面代表具有三个属性的基本用户对象:

username,这是表示用户的用户名的字符串。 email,它是表示用户的电子邮件地址的字符串。 password,它是表示用户密码的字符串。

  • UnitUser 接口扩展了 User 接口并添加了 id 属性:

id,是一个字符串,代表用户的唯一标识符。

  • Users 接口表示具有动态键的用户对象的集合:

[key: string]表示Users对象的键可以是任意字符串。 Users 对象的值属于 UnitUser 类型,这意味着集合中的每个用户对象都应符合 UnitUser 接口。 简单来说,这些接口定义了用户对象的结构和类型。User 接口定义了用户的基本属性,而 UnitUser 接口添加了 id 属性来表示具有唯一标识符的用户。Users 接口表示用户对象的集合,其中键是字符串,值是 UnitUser 对象。

接下来,我们将为数据存储创建逻辑。如果您愿意,您可以将其称为数据库。 使用以下代码填充 src/users/user.database.ts:

 import { User, UnitUser, Users } from "./user.interface";
 import bcrypt from "bcryptjs"
 import {v4 as random} from "uuid"
 import fs from "fs"
 
 let users: Users = loadUsers()
 
 function loadUsers () : Users {
   try {
     const data = fs.readFileSync("./users.json", "utf-8")
     return JSON.parse(data)
   } catch (error) {
     console.log(`Error ${error}`)
     return {}
   }
 }
 
 function saveUsers () {
   try {
     fs.writeFileSync("./users.json", JSON.stringify(users), "utf-8")
     console.log(`User saved successfully!`)
   } catch (error) {
     console.log(`Error : ${error}`)
   }
 }
 
 export const findAll = async (): Promise<UnitUser[]> => Object.values(users);
 
 export const findOne = async (id: string): Promise<UnitUser> => users[id];
 
 export const create = async (userData: UnitUser): Promise<UnitUser | null> => {
 
   let id = random()
 
   let check_user = await findOne(id);
 
   while (check_user) {
     id = random()
     check_user = await findOne(id)
   }
 
   const salt = await bcrypt.genSalt(10);
 
   const hashedPassword = await bcrypt.hash(userData.password, salt);
 
   const user : UnitUser = {
     id : id,
     username : userData.username,
     email : userData.email,
     password: hashedPassword
   };
 
   users[id] = user;
 
   saveUsers()
 
   return user;
 };
 
 export const findByEmail = async (user_email: string): Promise<null | UnitUser> => {
 
   const allUsers = await findAll();
 
   const getUser = allUsers.find(result => user_email === result.email);
 
   if (!getUser) {
     return null;
   }
 
   return getUser;
 };
 
 export const comparePassword  = async (email : string, supplied_password : string) : Promise<null | UnitUser> => {
 
     const user = await findByEmail(email)
 
     const decryptPassword = await bcrypt.compare(supplied_password, user!.password)
 
     if (!decryptPassword) {
         return null
     }
 
     return user
 }
 
 export const update = async (id : string, updateValues : User) : Promise<UnitUser | null> => {
 
     const userExists = await findOne(id)
 
     if (!userExists) {
         return null
     }
 
     if(updateValues.password) {
         const salt = await bcrypt.genSalt(10)
         const newPass = await bcrypt.hash(updateValues.password, salt)
 
         updateValues.password = newPass
     }
 
     users[id] = {
         ...userExists,
         ...updateValues
     }
 
     saveUsers()
 
     return users[id]
 }
 
 export const remove = async (id : string) : Promise<null | void> => {
 
     const user = await findOne(id)
 
     if (!user) {
         return null
     }
 
     delete users[id]
 
     saveUsers()
 }


让我解释一下上面代码中的每个函数:

loadUsers:此函数使用 fs 模块从名为“users.json”的文件中读取数据。它尝试将数据解析为 JSON 并将其作为用户对象返回。如果在此过程中发生错误,它会记录错误并返回一个空对象。

saveUsers:此函数通过使用 fs 模块的 writeFileSync 方法写入用户对象的 JSON 字符串表示形式,将用户对象保存到“users.json”文件中。如果在此过程中发生错误,则会记录该错误。

findAll:此函数返回一个解析为 UnitUser 对象数组的承诺。它使用 Object.values(users) 从用户对象中提取值(用户)。

findOne:此函数采用 id 参数并返回一个 Promise,该 Promise 解析为与 users 对象中的该 id 对应的 UnitUser 对象。

create:此函数将 userData 对象作为输入,并返回一个解析为新创建的 UnitUser 对象的 Promise。它使用 uuid 包生成一个随机 ID,并检查具有该 ID 的用户是否已存在。如果具有该 id 的用户存在,它将生成一个新的 id,直到找到唯一的 id。然后,它使用 bcrypt 对 userData 对象的密码进行哈希处理,并将哈希后的密码保存在 UnitUser 对象中。UnitUser 对象被添加到 users 对象中,使用 saveUsers 保存并返回。

findByEmail:此函数采用 user_email 参数,并返回一个承诺,如果具有指定电子邮件的用户存在,则该承诺解析为 UnitUser 对象,否则返回 null。它使用 findAll 检索所有用户,并使用 find 方法查找具有匹配电子邮件地址的用户。

ComparePassword:此函数采用电子邮件和提供的密码作为参数,如果提供的密码与用户存储的密码匹配,则返回一个解析为 UnitUser 对象的承诺,否则返回 null。它调用 findByEmail 通过电子邮件检索用户,然后使用 bcrypt.compare 将散列存储的密码与提供的密码进行比较。

update:此函数采用 id 和 updateValues 作为参数,并返回一个承诺,如果具有指定 id 的用户存在,则该承诺解析为更新的 UnitUser 对象。它使用 findOne 检查用户是否存在,如果 updateValues 包含新密码,则更新用户的密码。用户的属性使用 updateValues 中的值进行更新,并使用 saveUsers 保存用户对象。

remove:此函数采用 id 参数并返回一个承诺,如果具有指定 id 的用户不存在,则该承诺解析为 null,否则返回 void。它使用 findOne 检查用户是否存在,并使用 delete 关键字从 users 对象中删除该用户。然后使用 saveUsers 保存更新的用户对象。

这些函数充当我们的 API 可用来处理和检索数据库信息的方法。

接下来,让 all 将所有必需的函数和模块导入到路由文件 ./src/users.routes.ts 中并填充如下:

 import express, {Request, Response} from "express"
 import { UnitUser, User } from "./user.interface"
 import {StatusCodes} from "http-status-codes"
 import * as database from "./user.database"
 
 export const userRouter = express.Router()
 
 userRouter.get("/users", async (req : Request, res : Response) => {
     try {
         const allUsers : UnitUser[] = await database.findAll()
 
         if (!allUsers) {
             return res.status(StatusCodes.NOT_FOUND).json({msg : `No users at this time..`})
         }
 
         return res.status(StatusCodes.OK).json({total_user : allUsers.length, allUsers})
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 userRouter.get("/user/:id", async (req : Request, res : Response) => {
     try {
         const user : UnitUser = await database.findOne(req.params.id)
 
         if (!user) {
             return res.status(StatusCodes.NOT_FOUND).json({error : `User not found!`})
         }
 
         return res.status(StatusCodes.OK).json({user})
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 userRouter.post("/register", async (req : Request, res : Response) => {
     try {
         const { username, email, password } = req.body
 
         if (!username || !email || !password) {
             return res.status(StatusCodes.BAD_REQUEST).json({error : `Please provide all the required parameters..`})
         }
 
         const user = await database.findByEmail(email)
 
         if (user) {
             return res.status(StatusCodes.BAD_REQUEST).json({error : `This email has already been registered..`})
         }
 
         const newUser = await database.create(req.body)
 
         return res.status(StatusCodes.CREATED).json({newUser})
 
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 userRouter.post("/login", async (req : Request, res : Response) => {
     try {
         const {email, password} = req.body
 
         if (!email || !password) {
             return res.status(StatusCodes.BAD_REQUEST).json({error : "Please provide all the required parameters.."})
         }
 
         const user = await database.findByEmail(email)
 
         if (!user) {
             return res.status(StatusCodes.NOT_FOUND).json({error : "No user exists with the email provided.."})
         }
 
         const comparePassword = await database.comparePassword(email, password)
 
         if (!comparePassword) {
             return res.status(StatusCodes.BAD_REQUEST).json({error : `Incorrect Password!`})
         }
 
         return res.status(StatusCodes.OK).json({user})
 
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 
 userRouter.put('/user/:id', async (req : Request, res : Response) => {
 
     try {
 
         const {username, email, password} = req.body
 
         const getUser = await database.findOne(req.params.id)
 
         if (!username || !email || !password) {
             return res.status(401).json({error : `Please provide all the required parameters..`})
         }
 
         if (!getUser) {
             return res.status(404).json({error : `No user with id ${req.params.id}`})
         }
 
         const updateUser = await database.update((req.params.id), req.body)
 
         return res.status(201).json({updateUser})
     } catch (error) {
         console.log(error)
         return res.status(500).json({error})
     }
 })
 
 userRouter.delete("/user/:id", async (req : Request, res : Response) => {
     try {
         const id = (req.params.id)
 
         const user = await database.findOne(id)
 
         if (!user) {
             return res.status(StatusCodes.NOT_FOUND).json({error : `User does not exist`})
         }
 
         await database.remove(id)
 
         return res.status(StatusCodes.OK).json({msg : "User deleted"})
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })


这是每个函数的作用:

userRouter.get("/users"):此函数处理对“/users”的 GET 请求。它从数据库模块调用 findAll 函数来检索所有用户。如果未找到用户,它将返回 404 状态代码和一条消息。如果找到用户,则返回 200 状态码以及用户总数和所有用户的数组。

userRouter.get("/user/:id"):此函数处理对“/user/:id”的 GET 请求,其中 :id 代表特定用户的 ID。它从数据库模块调用 findOne 函数来检索具有指定 ID 的用户。如果未找到用户,则会返回 404 状态代码和错误消息。如果找到用户,它将返回 200 状态代码以及用户对象。

userRouter.post("/register"):此函数处理对“/register”的 POST 请求以进行用户注册。它从请求正文中提取用户名、电子邮件和密码。如果缺少任何这些字段,它将返回 400 状态代码和错误消息。它从数据库模块调用 findByEmail 函数来检查电子邮件是否已注册。如果找到电子邮件,它会返回 400 状态代码和错误消息。如果未找到电子邮件,它将调用数据库模块中的创建函数来创建新用户,并返回 201 状态代码和新创建的用户对象。

userRouter.post("/login"):此函数处理对“/login”的 POST 请求以进行用户登录。它从请求正文中提取电子邮件和密码。如果缺少任何这些字段,它将返回 400 状态代码和错误消息。它从数据库模块调用 findByEmail 函数来检查电子邮件是否存在。如果未找到电子邮件,则会返回 404 状态代码和错误消息。如果找到电子邮件,它将调用数据库模块中的comparePassword 函数来检查提供的密码是否与存储的密码匹配。如果密码不匹配,它将返回 400 状态代码和错误消息。如果密码匹配,它将返回 200 状态代码以及用户对象。

userRouter.put('/user/:id'):此函数处理对“/user/:id”的 PUT 请求,其中 :id 代表特定用户的 ID。它从请求正文中提取用户名、电子邮件和密码。如果缺少任何这些字段,它将返回 401 状态代码和错误消息。它从数据库模块调用findOne函数来检查具有指定ID的用户是否存在。如果未找到用户,则会返回 404 状态代码和错误消息。如果找到用户,它会从数据库模块调用更新函数来更新用户的详细信息,并返回 201 状态代码和更新后的用户对象。

userRouter.delete("/user/:id"):此函数处理对“/user/:id”的 DELETE 请求,其中 :id 代表特定用户的 ID。它从请求参数中提取 id。它从数据库模块调用findOne函数来检查具有指定ID的用户是否存在。如果未找到用户,则会返回 404 状态代码和错误消息。如果找到用户,它会调用数据库模块中的删除函数来删除该用户,并返回 200 状态代码和成功消息。

所有这些函数定义了与用户相关的操作的路由和相应的逻辑,例如检索所有用户、检索特定用户、注册新用户、登录用户、更新用户详细信息和删除用户。

最后,为了对这些路由进行 API 调用,我们需要将它们导入到我们的 app.ts 文件中并更新我们的代码,如下所示:

 import express from "express"
 import * as dotevnv from "dotenv"
 import cors from "cors"
 import helmet from "helmet"
 import { userRouter } from "./users/users.routes"
 
 dotevnv.config()
 
 if (!process.env.PORT) {
     console.log(`No port value specified...`)
 }
 
 const PORT = parseInt(process.env.PORT as string, 10)
 
 const app = express()
 
 app.use(express.json())
 app.use(express.urlencoded({extended : true}))
 app.use(cors())
 app.use(helmet())
 
 app.use('/', userRouter)
 
 app.listen(PORT, () => {
     console.log(`Server is listening on port ${PORT}`)
 })


伟大的!现在让我们启动服务器并使用 Postman 测试我们的 API。

npm run dev在你的终端中运行

你的终端应该与此类似

 [INFO] 20:55:40 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.1, typescript ver. 5.1.3)Server is listening on port 7000

伟大的!让我们调用我们的端点。

注册用户


登录用户


获取所有用户


获取单个用户


更新用户


删除用户:


注意:如果您添加了用户,您的 users.json 文件应不断追加新用户,并且应如下所示。

用户数据存储文件:


最后,让我们为我们的产品创建登录和路由。 因此,让我们将用户界面的内容复制到文件中并进行少量更改./src/product.interface.ts

 export interface Product {
     name : string,
     price : number;
     quantity : number;
     image : string;
 }
 
 export interface UnitProduct extends Product {
     id : string
 }
 
 export interface Products {
     [key : string] : UnitProduct
 }


您可以参考有关用户界面的部分,了解有关这些界面的用途的详细信息。

接下来,就像在文件中一样,让我们用类似的逻辑./src/users.database.ts填充 。./src/products.database.ts

 import { Product, Products, UnitProduct } from "./product.interface";
 import { v4 as random } from "uuid";
 import fs from "fs";
 
 let products: Products = loadProducts();
 
 function loadProducts(): Products {
   try {
     const data = fs.readFileSync("./products.json", "utf-8");
     return JSON.parse(data);
   } catch (error) {
     console.log(`Error ${error}`);
     return {};
   }
 }
 
 function saveProducts() {
     try {
         fs.writeFileSync("./products.json", JSON.stringify(products), "utf-8");
         console.log("Products saved successfully!")
     } catch (error) {
         console.log("Error", error)
     }
 }
 
 
 export const findAll = async () : Promise<UnitProduct[]> => Object.values(products)
 
 export const findOne = async (id : string) : Promise<UnitProduct> => products[id]
 
 export const create = async (productInfo : Product) : Promise<null | UnitProduct> => {
 
     let id = random()
 
     let product = await findOne(id)
 
     while (product) {
         id = random ()
         await findOne(id)
     }
 
     products[id] = {
         id : id,
         ...productInfo
     }
 
     saveProducts()
 
     return products[id]
 }
 
 export const update = async (id : string, updateValues : Product) : Promise<UnitProduct | null> => {
 
     const product = await findOne(id)
 
     if (!product) {
         return null
     }
 
     products[id] = {
         id,
         ...updateValues
     }
 
     saveProducts()
 
     return products[id]
 }
 
 export const remove = async (id : string) : Promise<null | void> => {
 
     const product = await findOne(id)
 
     if (!product) {
         return null
     }
 
     delete products[id]
 
     saveProducts()
 
 }


同样,您可以参考用户部分,了解有关这些函数为我们的 API 提供的功能的更多详细信息。

一旦我们的逻辑检查通过,就可以为我们的产品实施路线了。

./src/products.routes.ts使用以下代码填充该文件:

 import express, {Request, Response} from "express"
 import { Product, UnitProduct } from "./product.interface"
 import * as database from "./product.database"
 import {StatusCodes} from "http-status-codes"
 
 export const productRouter = express.Router()
 
 productRouter.get('/products', async (req : Request, res : Response) => {
     try {
        const allProducts = await database.findAll()
 
        if (!allProducts) {
         return res.status(StatusCodes.NOT_FOUND).json({error : `No products found!`})
        }
 
        return res.status(StatusCodes.OK).json({total : allProducts.length, allProducts})
     } catch (error) {
        return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 productRouter.get("/product/:id", async (req : Request, res : Response) => {
     try {
         const product = await database.findOne(req.params.id)
 
         if (!product) {
             return res.status(StatusCodes.NOT_FOUND).json({error : "Product does not exist"})
         }
 
         return res.status(StatusCodes.OK).json({product})
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 
 productRouter.post("/product", async (req : Request, res : Response) => {
     try {
         const {name, price, quantity, image} = req.body
 
         if (!name || !price || !quantity || !image) {
             return res.status(StatusCodes.BAD_REQUEST).json({error : `Please provide all the required parameters..`})
         }
         const newProduct = await database.create({...req.body})
         return res.status(StatusCodes.CREATED).json({newProduct})
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 productRouter.put("/product/:id", async (req : Request, res : Response) => {
     try {
         const id = req.params.id
 
         const newProduct = req.body
 
         const findProduct = await database.findOne(id)
 
         if (!findProduct) {
             return res.status(StatusCodes.NOT_FOUND).json({error : `Product does not exist..`})
         }
 
         const updateProduct = await database.update(id, newProduct)
 
         return res.status(StatusCodes.OK).json({updateProduct})
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })
 
 
 productRouter.delete("/product/:id", async (req : Request, res : Response) => {
     try {
         const getProduct = await database.findOne(req.params.id)
 
         if (!getProduct) {
             return res.status(StatusCodes.NOT_FOUND).json({error : `No product with ID ${req.params.id}`})
         }
 
         await database.remove(req.params.id)
 
         return res.status(StatusCodes.OK).json({msg : `Product deleted..`})
 
     } catch (error) {
         return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({error})
     }
 })


不要忘记在我们的 app.ts 文件中导入并调用产品的路由,该文件现在应该如下所示:

 import express from "express"
 import * as dotevnv from "dotenv"
 import cors from "cors"
 import helmet from "helmet"
 import { userRouter } from "./users/users.routes"
 import { productRouter } from "./products/product.routes"
 
 dotevnv.config()
 
 if (!process.env.PORT) {
     console.log(`No port value specified...`)
 }
 
 const PORT = parseInt(process.env.PORT as string, 10)
 
 const app = express()
 
 app.use(express.json())
 app.use(express.urlencoded({extended : true}))
 app.use(cors())
 app.use(helmet())
 
 app.use('/', userRouter)
 app.use('/', productRouter)
 
 app.listen(PORT, () => {
     console.log(`Server is listening on port ${PORT}`)
 })


完美的。我们现在拥有一个使用 Typescript 和 Nodejs 构建的成熟 API。欢呼!!

让我们测试一下我们的端点。

创建产品


所有产品


单品


更新产品


删除产品


如果您添加新产品,它们将被附加到products.json文件中,如下所示:


我们就这样完成了。如果您已经走到这一步,恭喜您并谢谢您!

欢迎提出意见和建议。

你可以在 github 上找到完整的代码 -> GITHUB:/REALSTEVEIG/REST-API-WITH-TYPESCRIPT-NODEJS-AND-A-FILE-BASED-STORAGE-SYSTEM

(更|多优质内|容:java567 点 c0m)


Node.js和TypeScript教程:使用Typescript、NodeJS和基于文件的存储系统构建REST API的评论 (共 条)

分享到微博请遵守国家法律