Three Options To Use Mongo With Node (And NestJS)
We will look into each and every one of them, and I will provide an example of how you can use MongoDB in your NestJS application in a headache-free way.
- The best tutorial I have found for NestJS + Mongoose is here. The issue I had with it is that I had to write the schema definitions and the typescript interfaces. If you are fine with writing everything two times, one for the Schema and one for the Document as Typescript Interface, maybe this is the best way to go!
- NestJS + TypeORM is where you can actually use TypeORM with MongoDB; however, I do not recommend this. If you want to learn more, I would ask you to read this blog post.
- NestJS + Typegoose — basically, it uses your domain objects to get the schema from them. And this is what this post is all about. There is a lot of documentation on how you can achieve that; however, I didn’t like most of it; it just looked like too much code. On top of that, ALL tutorials ALWAYS include using a DTO class, and I don’t see a reason to use DTO classes at all in 99% of the cases. DTOs are great, don’t get me wrong, there are many pros of using DTOs, but none of the tutorials on the internet actually explains why they want DTOs, and in fact, none of them need DTOs so I would like to write the easiest straightforward way with NestJS + MongoDB + and TypeGoose.
So, first of all, we will install NestJS CLI, NestJS is very, very similar to Angular, and even if you don’t like Angular, trust me, you will like NestJS since I am actually the same! For a great beginner’s tutorial for NestJS, you can read here find.
Let’s Start by Creating the NestJS App
npm i -g @nestjs/cli
Then create a NestJS project.
nest new nestjspoc-nest cd nestjspoc-nest // start the application using nodemon npm run start:dev
Open the browser to localhost:3000 to verify hello world is displayed.
We will create a simple service and controller in a module. Let’s say our applications will do something with users, and we will want to create a UserModule, which will hold the user domain objects, user services, and user controllers.
nest generate module user
nest generate service user
nest generate controller user
Now you should have a folder that has UserModule
, UserService
, and UserController
.
Which are almost empty.
For nest, we will use nestjs-typegoose because it makes everything even easier.
npm install — save nestjs-typegoose
The typegoose has several peer dependencies, so we need to install them as well. We have the two nestjs dependencies we already have, but we need the other two.
"@typegoose/typegoose": "^6.0.0", "@nestjs/common": "^6.3.1", "@nestjs/core": "^6.3.1", "mongoose": "^5.5.13"
Once you are done, your package.json should look like this:
{ "name": "nestjspoc", "version": "0.0.1", "description": "", "author": "", "license": "MIT", "scripts": { "prebuild": "rimraf dist", "build": "nest build", "format": "prettier --write "src/**/*.ts" "test/**/*.ts"", "start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "tslint -p tsconfig.json -c tslint.json", "test": "jest", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { "@nestjs/common": "^6.7.2", "@nestjs/core": "^6.7.2", "@nestjs/platform-express": "^6.7.2", "nestjs-typegoose": "^7.0.0", "rimraf": "^3.0.0", "rxjs": "^6.5.3", "@typegoose/typegoose": "^6.0.0", "mongoose": "^5.5.13" }, "devDependencies": { "@nestjs/cli": "^6.9.0", "@nestjs/schematics": "^6.7.0", "@nestjs/testing": "^6.7.1", "@types/express": "^4.17.1", "@types/jest": "^24.0.18", "@types/node": "^12.7.5", "@types/supertest": "^2.0.8", "jest": "^24.9.0", "prettier": "^1.18.2", "supertest": "^4.0.2", "ts-jest": "^24.1.0", "ts-loader": "^6.1.1", "ts-node": "^8.4.1", "tsconfig-paths": "^3.9.0", "tslint": "^5.20.0", "typescript": "^3.6.3" }, "jest": { "moduleFileExtensions": [ "js", "json", "ts" ], "rootDir": "src", "testRegex": ".spec.ts$", "transform": { "^.+\.(t|j)s$": "ts-jest" }, "coverageDirectory": "./coverage", "testEnvironment": "node" } }
The ones we added manually are in bold.
Well, this is the setup; let’s write some code.
Create your domain object user in a file user.ts, for example :
import {prop, Typegoose} from '@typegoose/typegoose'; export class User extends Typegoose { @prop() name?: string; }
You see the @prop()
yup, you need this. You can learn more about validations and what you can do in the typegoose documentation.
Then let’s create or update our UserService class.
Ok, the first magic is actually here!
You may notice the line @InjectModel(User) private readonly userModel: ReturnModelType
, this will give us a userModel
that we can use for our User type.
The createCustomUser
and listUsers
use this userModel
and I believe it is all clear HOW 🙂
Next, update our UserController
.
Nothing fancy here; we just inject our Service, and we call our two methods.
Some more magic!
There are two more magic lines that you need to add to your UserModule
and AppModule.
On the UserModule definition, we need this line — imports: [TypegooseModule.forFeature([User])]:
import { Module } from '@nestjs/common'; import { UserController } from './user.controller'; import { UserService } from './user.service'; import {User} from './domain/user'; import {TypegooseModule} from 'nestjs-typegoose'; @Module({ imports: [TypegooseModule.forFeature([User])], controllers: [UserController], providers: [UserService], }) export class UserModule {}
And on the AppModule
, we need the Mongoose configuration of the MongoDB connection string imports:
imports: [TypegooseModule.forRoot(‘mongodb://localhost:27017/nest’), UserModule],
import {Module} from '@nestjs/common'; import {AppController} from './app.controller'; import {AppService} from './app.service'; import {UserModule} from './user/user.module'; import {TypegooseModule} from 'nestjs-typegoose'; @Module({ imports: [TypegooseModule.forRoot('mongodb://localhost:27017/nest'), UserModule], controllers: [AppController], providers: [AppService], }) export class AppModule { }
And yes, you need MongoDB to be running 😉
Well, that’s it!
Create a User:
curl -X POST http://localhost:3000/user/createuser -H ‘Content-Type: application/json’ -d ‘{ “name”: “Nayden Gochev” }’
And you will receive:
{“_id”:”5dc00795d9a25df587a1e5f9",”name”:”Nayden Gochev”,”__v”:0}
List All Users:
curl -X GET http://localhost:3000/user/listusers
Missing Bits
What is missing? Validation, Security, and Testing, of course, all of this — next time 😉 now was the nest time.
Source Code
The full source code can be downloaded here.