NodeJS之NestJS整合NeDB

Nest 简介

Nest是一个NodeJS的服务端框架,与JAVA对照的话,其功能相当于SpringMVC(国产也有类似框架,如阿里开源的EggJS)。对于熟悉JAVA的开发者,Nest极易上手,Nest有着与SpringMVC如出一辙的分层架构和注解标签,以及依赖注入、控制反转、生命周期等实现,官网传送门

NeDB简介

NeDB是一个NodeJS实现的轻量级、嵌入式、NoSQL数据库,可用作内存数据库,也可以持久化。使用简单、速度快,适合数据量小、业务简单的场景(https://www.npmjs.com/package/nedb)。

创建项目,添加依赖

创建Nest项目,添加NeDB依赖,并将初始化项目按业务模块调整。

创建Nest项目

创建项目nest-demo:

nest new nest-demo

安装NeDB:

cd nest-demo
npm i nedb -S

调整初始化项目

将初始化的项目结构按业务模块调整,调整后的结构如下(包含两个模块,book和library):

app.module.ts

import { Module } from '@nestjs/common';
import { BookModule } from './book/book.module';
import { LibraryModule } from './library/library.module';

@Module({
  imports: [BookModule, LibraryModule],
  controllers: [],
  providers: [],
})
export class AppModule {}

整合NeDB,实现CRUD

book.entity.ts,book模型

export interface BookEntity {
  _id: string;
  name: string;
  price: number;
  author: string;
  createdAt: number;
  updatedAt: number;
}

book.controller.ts

import { Body, Controller, Get, Post } from '@nestjs/common';
import { BookService } from './book.service';
import { BookEntity } from './book.entity';

@Controller('book')
export class BookController {
  constructor(private readonly bookService: BookService) {
    this.bookService = bookService;
  }

  /**
   * list all books
   */
  @Get()
  async list(): Promise<Array<BookEntity>> {
    return this.bookService.list();
  }

  @Get(':id')
  async detail(@Param('id') _id: string): Promise<BookEntity> {
    return this.bookService.detail(_id);
  }

  /**
   * save new book
   * @param book new book data
   */
  @Post()
  async save(@Body() book: BookEntity): Promise<BookEntity> {
    return this.bookService.save(book);
  }
}

book.service.ts

import { Injectable } from '@nestjs/common';
import { BookEntity } from './book.entity';

const Nedb = require('nedb');

@Injectable()
export class BookService {
  private readonly nedb;
  constructor() {
    this.nedb = new Nedb({
      filename: './db/book.db',
      autoload: true,
      timestampData: true,
    });
  }

  /**
   * find all books
   */
  async list(): Promise<Array<BookEntity>> {
    return new Promise((resolve, reject) => {
      this.nedb.find({}, (error, docs) => {
        if (error) reject(error);
        resolve(docs);
      });
    });
  }

  async detail(_id: string): Promise<BookEntity> {
    return new Promise((resolve, reject) => {
      this.nedb.find({ _id }, (error, docs) => {
        if (error) reject(error);
        resolve(docs[0]);
      });
    });
  }

  /**
   * save new book
   * @param book new book
   */
  async save(book: BookEntity): Promise<BookEntity> {
    return new Promise((resolve, reject) => {
      this.nedb.insert(book, (error, doc) => {
        if (error) reject(error);
        resolve(doc);
      });
    });
  }
}

重构代码,封装公共基础类,通过继承实现复用和扩展

业务与数据分离,repository处理数据

app.repository.ts,基础repository,实现CRUD操作

// eslint-disable-next-line @typescript-eslint/no-var-requires
const Nedb = require('nedb');

export class AppRepository<T> {
  protected readonly nedb;

  constructor(dbName: string) {
    this.nedb = new Nedb({
      filename: dbName,
      autoload: true,
      timestampData: true,
    });
  }

  /**
   * find all
   */
  async findAll(): Promise<Array<T>> {
    return this.find({});
  }

  /**
   * find by primary key
   * @param _id primary key
   */
  async findById(_id: string): Promise<T> {
    return new Promise((resolve, reject) => {
      this.find({ _id })
        .then(res => resolve(res[0]))
        .catch(error => reject(error));
    });
  }

  /**
   * find by options
   * @param options search options
   */
  async find(options): Promise<Array<T>> {
    return new Promise((resolve, reject) => {
      this.nedb.find(options, (error, docs) => {
        if (error) {
          reject(error);
        }
        resolve(docs);
      });
    });
  }

  /**
   * insert new data
   * @param data new data
   */
  async insert(data: T): Promise<T> {
    return new Promise((resolve, reject) => {
      this.nedb.insert(data, (error, doc) => {
        if (error) {
          reject(error);
        }
        resolve(doc);
      });
    });
  }

  /**
   * delete by id
   * @param _id
   */
  async deleteById(_id: string): Promise<number> {
    return new Promise((resolve, reject) => {
      this.nedb.remove({ _id }, (error, removedNum) => {
        if (error) {
          reject(error);
        }
        resolve(removedNum);
      });
    });
  }
}

book.repository.ts,实现复杂数据操作

import { Injectable } from '@nestjs/common';
import { AppRepository } from '../app.repository';
import { BookEntity } from './book.entity';

@Injectable()
export class BookRepository extends AppRepository<BookEntity>{
  constructor() {
    super('./db/book.db');
  }

  //其他复杂数据操作
  // ... ...
}

重写service,封装基础service类并将repository作为依赖引入

app.service.ts,service基类,依赖repository基类

import { AppRepository } from './app.repository';

export class AppService<T> {
  constructor(private readonly appRepository: AppRepository<T>) {
    this.appRepository = appRepository;
  }

  /**
   * list all
   */
  async list(): Promise<Array<T>> {
    return this.appRepository.findAll();
  }

  /**
   * get detail
   * @param _id
   */
  async detail(_id: string): Promise<T> {
    return this.appRepository.findById(_id);
  }

  /**
   * save new data
   * @param data
   */
  async save(data: T): Promise<T> {
    return this.appRepository.insert(data);
  }

  /**
   * delete by id
   * @param _id
   */
  async delete(_id: string): Promise<number> {
    return this.appRepository.deleteById(_id);
  }
}

重写book.service.ts,依赖book.repository.ts

import { Injectable } from '@nestjs/common';
import { AppService } from '../app.service';
import { BookEntity } from './book.entity';
import { BookRepository } from './book.repository';

@Injectable()
export class BookService extends AppService<BookEntity>{
  constructor(private readonly bookRepository: BookRepository) {
    super(bookRepository);
    this.bookRepository = bookRepository;
  }

  // 除CRUD外复杂业务逻辑
  // ... ...
}

同理,重写controller,封装controller基类

app.controller.ts

import { AppService } from './app.service';
import { Body, Delete, Get, Param, Post } from '@nestjs/common';

export class AppController<T> {
  constructor(private readonly appService: AppService<T>) {
    this.appService = appService;
  }

  @Get()
  async list(): Promise<Array<T>> {
    return this.appService.list();
  }

  @Get(':id')
  async detail(@Param('id') _id: string): Promise<T> {
    return this.appService.detail(_id);
  }

  @Post()
  async save(@Body() data: T): Promise<T> {
    return this.appService.save(data);
  }

  @Delete(':id')
  async delete(@Param('id') id: string): Promise<number> {
    return this.appService.delete(id);
  }
}

重写book.controller.ts,继承controller基类

import { Controller } from '@nestjs/common';
import { AppController } from '../app.controller';
import { BookEntity } from './book.entity';
import { BookService } from './book.service';

@Controller('book')
export class BookController extends AppController<BookEntity>{
  constructor(private readonly bookService: BookService) {
    super(bookService);
    this.bookService = bookService;
  }
  
  // 其他复杂逻辑
  // ... ...
}

book.module.ts,将book.repository引入

import { Module } from '@nestjs/common';
import { BookController } from './book.controller';
import { BookService } from './book.service';
import { BookRepository } from './book.repository';

@Module({
  imports: [],
  controllers: [BookController],
  providers: [BookService, BookRepository],
})
export class BookModule {}

至此,book模块调整完成,terminal执行 npm run start启动服务便可通过http://127.0.0.1:300/book访问相关API。

library模块实现,只需继承app.controller.ts,app.service.ts和app.repository.ts基类便可完成CRUD功能

library.module.ts

import { Module } from '@nestjs/common';
import { LibraryController } from './library.controller';
import { LibraryService } from './library.service';
import { LibraryRepository } from './library.repository';

@Module({
  imports: [],
  controllers: [LibraryController],
  providers: [LibraryService, LibraryRepository],
})
export class LibraryModule {}

library.controller.ts

import { Controller, Injectable } from '@nestjs/common';
import { AppController } from '../app.controller';
import { LibraryEntity } from './library.entity';
import { LibraryService } from './library.service';

@Controller('library')
export class LibraryController extends AppController<LibraryEntity> {
  constructor(private readonly libraryService: LibraryService) {
    super(libraryService);
    this.libraryService = libraryService;
  }

  // other code ...
}

library.service.ts

import { Injectable } from '@nestjs/common';
import { AppService } from '../app.service';
import { LibraryEntity } from './library.entity';
import { LibraryRepository } from './library.repository';

@Injectable()
export class LibraryService extends AppService<LibraryEntity> {
  constructor(private readonly libraryRepository: LibraryRepository) {
    super(libraryRepository);
    this.libraryRepository = libraryRepository;
  }

  // other code ...
}

library.repository.ts

import { AppRepository } from '../app.repository';
import { LibraryEntity } from './library.entity';
import { Injectable } from '@nestjs/common';

@Injectable()
export class LibraryRepository extends AppRepository<LibraryEntity> {
  constructor() {
    super('./db/library.db');
  }
  // other code ...
}

启动服务,访问http://127.0.0.1/library即可调用library CRUD API。

工程源码请访问https://github.com/louie-001/nestjs-nedb-demo.git

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容