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

2025-05-26

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

介绍

欢迎来到我的博客!在本教程中,我将指导您使用 Node.js、Express 和 TypeScript 构建一个强大的微型电商 API。我们将一起探索各种功能和技术,帮助您为电商应用程序创建强大的 API。

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

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

您将获得使用涵盖用户和商品数据功能的实践经验,并了解这些实体如何在电商 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
Enter fullscreen mode Exit fullscreen mode

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

PORT=7000
Enter fullscreen mode Exit fullscreen mode

接下来,在 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}`)
})
Enter fullscreen mode Exit fullscreen mode

这段代码片段使用 Express 框架搭建了一个 Node.js 应用。具体流程如下:

导入所需的模块:

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

导入dotenv来处理环境变量。

导入cors以启用跨域资源共享。

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

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

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

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

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

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

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

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

helmet()用于通过设置各种 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:这是应用程序的入口文件。

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

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
}
Enter fullscreen mode Exit fullscreen mode

这段代码定义了三个 TypeScript 接口:

  • User 接口表示具有三个属性的基本用户对象:

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()
}


Enter fullscreen mode Exit fullscreen mode

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

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

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

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

findOne:此函数接受一个 id 参数并返回一个承诺,该承诺解析为用户对象中与该 id 对应的 UnitUser 对象。

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

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

comparePassword:此函数接受电子邮件和 provided_pa​​ssword 作为参数,并返回一个 promise,如果提供的密码与用户存储的密码匹配,则该 promise 解析为 UnitUser 对象,否则返回 null。它调用 findByEmail 通过电子邮件检索用户,然后使用 bcrypt.compare 将哈希存储的密码与提供的密码进行比较。

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

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

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

接下来,将所有必需的功能和模块导入到路由文件 ./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})
    }
})
Enter fullscreen mode Exit fullscreen mode

每个函数的作用如下:

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 状态码和错误消息。如果未找到邮箱,则调用数据库模块中的 create 函数来创建新用户,并返回 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 状态代码和错误消息。如果找到了用户,它将调用数据库模块中的 update 函数来更新用户的详细信息,并返回 201 状态代码和更新后的用户对象。

userRouter.delete("/user/:id"):此函数处理对“/user/:id”的 DELETE 请求,其中 :id 代表特定用户的 ID。它从请求参数中提取 ID。然后调用数据库模块中的 findOne 函数来检查是否存在具有指定 ID 的用户。如果未找到该用户,则返回 404 状态码和错误消息。如果找到了该用户,则调用数据库模块中的 remove 函数删除该用户,并返回 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}`)
})
Enter fullscreen mode Exit fullscreen mode

太棒了!现在让我们启动服务器并使用 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
}
Enter fullscreen mode Exit fullscreen mode

您可以参考用户界面部分来了解这些界面功能的详细信息。

接下来,就像在文件中一样,让我们​​用类似的逻辑./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()

}

Enter fullscreen mode Exit fullscreen mode

再次,您可以参考用户部分来了解这些功能为我们的 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})
    }
})
Enter fullscreen mode Exit fullscreen mode

不要忘记在我们的 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}`)
})

Enter fullscreen mode Exit fullscreen mode

完美!我们现在有了一个用 Typescript 和 Nodejs 构建的完整 API。太棒了!

让我们测试我们的端点。

创建产品

创建产品

所有产品

所有产品

单品

单品

更新产品

更新产品

删除产品

删除产品

如果您添加新产品,它们将被附加到products.json文件中,它看起来像这样:

Products.json 文件

好了,我们完成了。如果你读到这里,恭喜你,也谢谢你!

欢迎提出意见和建议。

您可以在 GitHub 上找到完整的代码 -> GITHUB

文章来源:https://dev.to/realsteveig/nodejs-and-typescript-tutorial-build-a-rest-api-with-typescript-nodejs-and-a-file-based-storage-system-2l61
PREV
作为一名代码新手,我后悔没读过的 5 本书
NEXT
使用 PHP 处理十亿行!