Typescript express 新手教程 S3 连接数据库 Model CRUD

MongoDB

为什么前端都要用这个DBMS。

太长不看

  • 用到mongoose库,是一个Object Data Modeling (ODM)
  • Model接受schema定义好的 表的形状,生成model实例,用于crud
  • 关于dotenv的使用,es6和commonjs是有一些区别,详见其官网介绍
  • 错误处理就是对可能发生(预期到)的错误用next传递给对应的错误处理器
    介绍MongoDB的使用
    PS:我要使用docker

使用.env 把一些不能提交到repo的信息给注入到项目运行时里

  1. 要把这个.env加入到.gitignore
  2. docker-compose 也会用到.env,一般来说,docker-compose的env 用于部署,所以不会和项目的.env共处,这里放到一块原因就是因为不会部署。

用mongoose 连接 MongoDB

怎么用docker来运行木地板数据库(笑),参见docker 运行MongoDB 极限简洁教程

在开启docker中的 MongoDB数据库容器之后,就可以在node中连接这个数据库,mongoose提供了.connect方法可以很方便的传入数据库Url 以及必要的验证信息,这个方法返回一个promise,如果发生任何连接问题,都可以清楚的在catch中处理和report。

//  https://www.typescriptlang.org/docs/handbook/module-resolution.html#module-resolution-strategies
// 这里把moduleResolution 明确地改为了 node (照理说默认应该就是node),至于这么改的到底 查看上述网址
import mongoose from "mongoose";
const { MONGO_URL, MONGO_USER, MONGO_PASS } = process.env;
mongoose
  .connect(MONGO_URL, {
    user: MONGO_USER,
    pass: MONGO_PASS,
  })
  .then(() => {
    //   MongoDB Database
    console.log("成功连接木地板数据库");
  })
  .catch((err) => {
    console.log(" connecting to the 木地板 database发生错误");
    console.log(err);
    process.exit();
  });

在这里就已经使用了.env注入到process.env里的配置信息,这是最佳实践之一。

ps:连接配置的格式

验证env参数的有效性

这同样也是最佳实践之一,env参数很有可能被错误的设置(typo最常见,因为没有智能提示),所以必须使用有效的工具来确保env是被正确地注入到运行时。使用 envalid库

介绍Model

MVC定义说的很严谨,model用于管理和处理数据,降到实际层面,model一般 至少抽象出了常用的CRUD方法,mongoose 是这里要使用到的类库,
它使用schema来定义表(这里叫collection)的形状,然后用定义后的形状生成model,最后用生成的model暴露的方法进行常规CRUD。

src/controller/post/post.model.ts

import mongoose, { Schema } from "mongoose";
import IPost from "./post.interface";

// https://mongoosejs.com/docs/typescript.html
// 1. Imports an interface representing a document in MongoDB.

// 2. Create a Schema corresponding to the document interface.
  const postSchema = new Schema<IPost>({
      content:{type:String,required:true},
      author:{type:String,required:true},
      title:{type:String,required:true},
  });



const postModel = mongoose.model<IPost & mongoose.Document>("Post", postSchema);

export default postModel;

这里暴露出的postModel上 就自带对数据库操作的抽象,可以直接在controller中使用model来操作数据库。
(而不是在controller来编写对数据库的操作逻辑)
还有一点需要注意,这个接口对Schema的约束属于,只关心存在的成员名称是否正确,而既不关心是否存在某个成员以及其required是否与接口中的类型相符

下面是使用model后的controller

import express, { Router } from "express";
import Controller from "../../interface/controller.interface";

import postModel from "./post.model";

export default class PostController implements Controller {
  router: Router;
  /**
   * 不应该对外暴露,防止被改变,只暴露一个getter
   */
  private childRoutePath: string = "/aChildRoute";

  constructor() {
    this.router = Router();
    this.useRoutes();
  }

  private useRoutes = () => {
    this.router.post(this.childRoutePath, this.createPost);
    this.router.get(this.childRoutePath, this.findPosts);
    this.router.get(`${this.childRoutePath}/:id`, this.findById);
    this.router.put(`${this.childRoutePath}/:id`, this.modifyPost);
    this.router.delete(`${this.childRoutePath}/:id`, this.deletePost);
  };

  private createPost = async (req: express.Request, res: express.Response) => {
    const post = req.body;
    await postModel.create(post);

    res.send(req.body);
  };
  private findPosts = async (req: express.Request, res: express.Response) => {
    const posts = await postModel.find();
    res.send(posts);
  };

  private findById = async (req: express.Request, res: express.Response) => {
    const id = req.params.id;
    const post = await postModel.findById(id);
    res.send(post);
  };
  private modifyPost = async (
    req: express.Request,
    res: express.Response,
    next: express.NextFunction
  ) => {
    const id = req.params.id;
    const newPost = req.body;
    postModel
      .findByIdAndUpdate(id, newPost, { new: true })
      .then((post) => {
        res.send(post);
      })
      .catch((err) => {
        next(err);
      });
  };
  private deletePost = async (
    req: express.Request,
    res: express.Response,
    next: express.NextFunction
  ) => {
    const id = req.params.id;

    postModel
      .findByIdAndDelete(id)
      .then((successResponse) => {
        if (successResponse) {
          res.send(200);
        } else {
          res.send(404);
        }
      })
      .catch((err) => {
        next(err);
      });
  };
  /**
   * 有可能会丢失 this
   */
  get path() {
    return this.childRoutePath;
  }
}

posted @ 2022-02-08 03:33  刘老六  阅读(222)  评论(0编辑  收藏  举报