使用typescript封装mongodb

4,250 阅读4分钟

使用typescript封装mongodb

疫情当前,大家足不出户,对于普通人来说,保护好身体,不生病,不给医护人员和国家添乱就是一种爱国的表现。

如果在这期间还能保持学习就是爱自己的表现。面对越来越严峻的互联网行业,自己也需要拓宽自己的视野,做好迎接挑战的准备。所以这段时间学了下koa和typescript,学完之后还是比较空洞,就像着看看能不能总结出自己的一套koa脚手架,首当其冲的一个任务就是用typescript封装mongodb。

为什么要封装mongodb

其实目前已经有很好的第三方框架了mongoose,这个已经非常好用了。为什么自己还要封装了。

  1. 当然是出于学习的目的
  2. 自己封装的可以更小,更灵活

准备工作

封装之前需要理解一下几个点

  1. typescript的语法(class, 泛型, 接口)
  2. 异步编程(promise, async, await)
  3. 设计模式(单例模式)
  4. node-mongodb-native第三库的使用。
  5. mongodb数据库语法

因为我是在node-mongodb-native的基础进行二次封装,所以肯定要了解这个框架的使用。git网址:github.com/mongodb/nod… 查看readme就能明白库的基本用法。只是下面一点需要注意

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';

// Database Name
const dbName = 'myproject';

// Use connect method to connect to the server
MongoClient.connect(url, function(err, client) {
  assert.equal(null, err);
  console.log("Connected successfully to server");

  const db = client.db(dbName);

  client.close();
});

这是官网连接数据库的例子,当你的数据库设置了用户权限这个就不适用。

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://jgmiu:jgmiu090840@127.0.0.1:27017/testdb'; // 这里跟上了数据库名以及该数据库的用户名以及密码

// Database Name
// const dbName = 'myproject'; 由于连接字符串跟上了数据库名,所以不需要这样

// Use connect method to connect to the server
MongoClient.connect(url, function(err, client) {
  assert.equal(null, err);
  console.log("Connected successfully to server");

  const db = client.db();// 这里也不需要跟上数据库名

  client.close();
});

我英文不好,所以在官方文档上没有查看到怎么连接由用户权限的方法,这个是我试出来的。

开始封装

准备工作做好之后下面开始一步一步开始封装。

创建config文件

新建config.ts的配置文件,配置如下

const config = {
    url: 'mongodb://jgmiu:jgmiu090840@127.0.0.1:27017/testdb'
 }

 export default config
引入第三方库

安装mongodb

npm install --save mongodb
DBI接口创建

新建db.ts文件,并进行接口编写如下

interface DBI {
    // 插入
    insert<T>(collectionName:string, doc:T):Promise<boolean>
    // 批量插入
    insertMany<T>(collectionName:string, docs:T[]):Promise<boolean>
    // 删除
    delete(collectionName:string, filter:object):Promise<boolean>
    // 更新
    update(collectionName:string, filter:object,update:object):Promise<boolean>
    // 查找
    find(collectionName:string, filter:object):Promise<any[]>
    // 高级查找
    aggregate(collectionName:string, pipeline:object[]):Promise<any[]>
}
创建单例模式的Db

为了保证数据库连接只有一个实例,所以我们自立必须使用单例模式

class Db {
    static instance:Db | null
    static getInstance() {
        if(!Db.instance) this.instance = new Db()
        return this.instance
    }
    constructor() {
        console.log('constructor')
    }
}

const db = Db.getinstance()
const db1 = Db.getinstance()

以上就是一个最简单的单例模式,db与db1是同一个实例

连接数据库

下面我们要连接数据库

import config from './config'
import mongodb from 'mongodb'

const MongoClient = mongodb.MongoClient

class Db implements DBI {
    public client:mongodb.MongoClient | undefined
    static instance:Db | null

    static getInstance() {
        if(!Db.instance) this.instance = new Db()
        return this.instance
    }

    constructor() {
        console.log('constructor')
        this.connection()
    }

    connection():Promise<mongodb.MongoClient> {
        return new Promise<mongodb.MongoClient>((resolve, reject) => {
            if(!this.client) {
                MongoClient.connect(config.url, {useUnifiedTopology:true}, (err, client) => {
                    if(err){
                        reject(err)
                    } else {
                        console.log('db server start success!')
                        this.client = client
                        resolve(client)
                    }
                })
            } else {
                resolve(this.client)
            }
        })
    }
}

有一下几点说明

  1. public client:mongodb.MongoClient | undefined 这里的client必须要申明为MongoClient类型,如果生命为any类型会编译不通过
  2. 连接数据库是一个比较耗时的操作,所以我们每次连接前我们判断this.client是否已经存在,存在我们就不会再去连接。
  3. 注意这里返回的是:Promise<mongodb.MongoClient>。后面很多方法我们都是返回的Promise

至此我们对多次实例与多次连接都做了相应的处理。

CRUD方法实现

我们准备工作做完了,接口定义好了,多次实例多次连接我们都做了相关处理,接下来就是实现增删改查了。这个过程虽然工作量比较大,但是也是最简单的。下面选一个增加的例子如下:

insert<T>(collectionName:string, doc:T):Promise<boolean> {
        return new Promise<boolean>((resolve, reject) => {
            this.connection().then((client) => {
                 const db = client.db()
                 const result = db.collection(collectionName).insert(doc, (err, result) => {
                     if(err){
                         resolve(false)
                         reject(err)
                     } else {
                         resolve(true)
                     }
                 })
            })
        })
    }
在koa中使用

koa使用了async语法,而我们封装的方法基本都是返回的promise,所以写起来就非常方便

import Koa from 'koa'
import Router from 'koa-router'
import Db from './module/db'

const app = new Koa(),
    router = new Router(),
    db = new Db()

router.get('/', async (ctx) => {
    const users = await db.find('users', {})
    console.log(users)
    // 测试添加

    interface User{
        name:string
    }
    const result = await db.insertMany<User>('users',[{name: '888'}])
    console.log(result)
    ctx.body = 'index'
})

app.use(router.routes())
app.use(router.allowedMethods())

app.listen(3000, ()=>{
    console.log('serve start on 3000')
})

到此完成。完整代码可以查看:github.com/miujg/koa-t…