IntroductionWelcome to the second part of our tutorial series on setting up Nx, Nest.js, MongoDB, and Mongoose integration. In this installment, we will guide you through the process of generating the feature-sample-todos library using Nx, creating modules, resolvers, and services, and establishing MongoDB document schemas. Additionally, we'll cover the implementation of GraphQL models, repositories, DTOs, and updates to the service and resolver for the sample-todos feature. Follow the step-by-step instructions to seamlessly integrate these technologies, enhancing the scalability and efficiency of your application. Stay tuned for a comprehensive configuration and integration journey.
Generating Feature-Sample-TodosTo begin, generate the feature-sample-todos library using Nx:
Copy npx nx generate @nx/js:library api-feature-sample-todos --directory=libs/api/mongoose/feature-sample-mongoose --importPath=@libs/api/mongoose/feature-sample-mongoose --tags=scope:api --bundler=swc
✔ Which unit test runner would you like to use? · jest
Creating Module, Resolver, and ServiceNow, create the module, resolver, and service for the sample-todos feature:
Copy nx generate @nx/nest:module sample-todos --project=api-feature-sample-todos
nx generate @nx/nest:resolver sample-todos --project=api-feature-sample-todos
nx generate @nx/nest:service sample-todos --project=api-feature-sample-todos
Once the files are created, move them to the lib folder.
Creating MongoDB Document (Schema)Create the document (schema) for MongoDB:
libs/api/feature-sample-todos/src/lib/sample-todos/models/sample-todos.schema.ts
Copy import { AbstractDocument } from '@libs/api/mongoose/shared' ;
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose' ;
@ Schema ({ versionKey: false })
export class SampleTodoDocument extends AbstractDocument {
@ Prop ()
content !: string ;
@ Prop ()
editing !: boolean ;
@ Prop ()
completed !: boolean ;
}
export const SampleTodoSchema = SchemaFactory. createForClass (SampleTodoDocument);
Creating GraphQL ModelCreate the model for GraphQL:
libs/api/feature-sample-todos/src/lib/sample-todos/models/sample-todos.model.ts
Copy import { AbstractModel } from '@libs/api/mongoose/shared' ;
import { Field, ObjectType } from '@nestjs/graphql' ;
@ ObjectType ()
export class SampleTodo extends AbstractModel {
@ Field ()
readonly content !: string ;
@ Field ()
readonly editing !: boolean ;
@ Field ()
readonly completed !: boolean ;
}
Creating RepositoryCreate the repository for sample-todos:
libs/api/feature-sample-todos/src/lib/sample-todos/sample-todos.repository.ts
Copy import { AbstractRepository } from '@libs/api/mongoose/shared' ;
import { Injectable, Logger } from '@nestjs/common' ;
import { InjectModel } from '@nestjs/mongoose' ;
import { Model } from 'mongoose' ;
import { SampleTodo } from './models/sample-todo.model' ;
import { SampleTodoDocument } from './models/sample-todo.schema' ;
@ Injectable ()
export class SampleTodosRepository extends AbstractRepository < SampleTodoDocument > {
protected readonly logger = new Logger (SampleTodosRepository.name);
constructor (
@ InjectModel (SampleTodo.name)
SampleTodoModel : Model < SampleTodoDocument >
) {
super (SampleTodoModel);
}
}
Creating DTOsCreate the DTOs for sample-todos:
Argslibs/api/feature-sample-todos/src/lib/sample-todos/dto/args/get-sample-todos-args.dto.ts
Copy import { ArgsType, Field } from '@nestjs/graphql' ;
import { IsNotEmpty, IsString } from 'class-validator' ;
@ ArgsType ()
export class GetSampleTodoArgs {
@ Field ()
@ IsNotEmpty ()
@ IsString ()
_id !: string ;
}
libs/api/feature-sample-todos/src/lib/sample-todos/dto/input/create-sample-todos-input.dto.ts
Copy import { Field, InputType } from '@nestjs/graphql' ;
import { IsBoolean, IsString } from 'class-validator' ;
@ InputType ()
export class CreateSampleTodoInput {
@ Field ()
@ IsString ()
content !: string ;
@ Field ()
@ IsBoolean ()
editing !: boolean ;
@ Field ()
@ IsBoolean ()
completed !: boolean ;
}
libs/api/feature-sample-todos/src/lib/sample-todos/dto/input/delete-sample-todos-input.dto.ts
Copy import { Field, InputType } from '@nestjs/graphql' ;
import { IsNotEmpty, IsString } from 'class-validator' ;
@ InputType ()
export class DeleteSampleTodoInput {
@ Field ()
@ IsNotEmpty ()
@ IsString ()
_id !: string ;
}
libs/api/feature-sample-todos/src/lib/sample-todos/dto/input/update-sample-todos-input.dto.ts
Copy import { Field, InputType } from '@nestjs/graphql' ;
import { IsBoolean, IsNotEmpty, IsString } from 'class-validator' ;
@ InputType ()
export class UpdateSampleTodoInput {
@ Field ()
@ IsNotEmpty ()
@ IsString ()
_id !: string ;
@ Field ()
@ IsString ()
content !: string ;
@ Field ()
@ IsBoolean ()
editing !: boolean ;
@ Field ()
@ IsBoolean ()
completed !: boolean ;
}
Updating ServiceUpdate the service for sample-todos:
libs/api/feature-sample-todos/src/lib/sample-todos/sample-todos.service.ts
Copy import { Injectable } from '@nestjs/common' ;
import { GetSampleTodoArgs } from './dto/args/get-sample-todo-args.dto' ;
import { CreateSampleTodoInput } from './dto/input/create-sample-todo-input.dto' ;
import { DeleteSampleTodoInput } from './dto/input/delete-sample-todo-input.dto' ;
import { UpdateSampleTodoInput } from './dto/input/update-sample-todo-input.dto' ;
import { SampleTodoDocument } from './models/sample-todo.schema' ;
import { SampleTodosRepository } from './sample-todos.repository' ;
@ Injectable ()
export class SampleTodosService {
constructor ( private readonly SampleTodoRepository : SampleTodosRepository ) {}
async createSampleTodo ( createSampleTodoData : CreateSampleTodoInput ) {
const SampleTodoDocument = await this .SampleTodoRepository. create ({
... createSampleTodoData
});
return this . toModel (SampleTodoDocument);
}
async updateSampleTodo ( updateSampleTodoData : UpdateSampleTodoInput ) {
const SampleTodoDocument = await this .SampleTodoRepository. findOneAndUpdate (
{ _id: updateSampleTodoData._id },
updateSampleTodoData
);
return this . toModel (SampleTodoDocument);
}
async deleteSampleTodo ( deleteSampleTodoData : DeleteSampleTodoInput ) {
return await this .SampleTodoRepository. deleteOne ({
_id: deleteSampleTodoData._id
});
}
async getSampleTodos () {
const SampleTodoDocument = await this .SampleTodoRepository. find ({});
return SampleTodoDocument. map (( todo ) => this . toModel (todo));
}
async getSampleTodo ( getSampleTodoArgs : GetSampleTodoArgs ) {
const SampleTodoDocument = await this .SampleTodoRepository. findOne ({
... getSampleTodoArgs
});
return this . toModel (SampleTodoDocument);
}
private toModel ( SampleTodoDocument : SampleTodoDocument ) {
return {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
_id: SampleTodoDocument._id. toHexString (),
... SampleTodoDocument
};
}
}
Updating ResolverUpdate the resolver for sample-todos:
libs/api/feature-sample-todos/src/lib/sample-todos/sample-todos.resolver.ts
Copy import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' ;
import { GetSampleTodoArgs } from './dto/args/get-sample-todo-args.dto' ;
import { CreateSampleTodoInput } from './dto/input/create-sample-todo-input.dto' ;
import { DeleteSampleTodoInput } from './dto/input/delete-sample-todo-input.dto' ;
import { UpdateSampleTodoInput } from './dto/input/update-sample-todo-input.dto' ;
import { SampleTodo } from './models/sample-todo.model' ;
import { SampleTodosService } from './sample-todos.service' ;
@ Resolver ()
export class SampleTodosResolver {
constructor ( private readonly SampleTodoService : SampleTodosService ) {}
@ Mutation (() => SampleTodo, { name: 'createSampleTodo' })
async createSampleTodo (
@ Args ( 'createSampleTodoData' )
createSampleTodoData : CreateSampleTodoInput
) {
return await this .SampleTodoService. createSampleTodo (createSampleTodoData);
}
@ Mutation (() => SampleTodo, { name: 'updateSampleTodo' })
async updateSampleTodo (
@ Args ( 'updateSampleTodoData' )
updateSampleTodoData : UpdateSampleTodoInput
) {
return this .SampleTodoService. updateSampleTodo (updateSampleTodoData);
}
@ Mutation (() => SampleTodo, { name: 'deleteSampleTodo' , nullable: true })
async deleteSampleTodo (
@ Args ( 'deleteSampleTodoData' )
deleteSampleTodoData : DeleteSampleTodoInput
) {
return this .SampleTodoService. deleteSampleTodo (deleteSampleTodoData);
}
@ Query (() => [SampleTodo], { name: 'sampleTodos' })
async getSampleTodos () {
return this .SampleTodoService. getSampleTodos ();
}
@ Query (() => SampleTodo, { name: 'sampleTodo' })
async getSampleTodo (@ Args () getSampleTodoArgs : GetSampleTodoArgs ) {
return this .SampleTodoService. getSampleTodo (getSampleTodoArgs);
}
}
Updating ModuleUpdate the module for sample-todos:
libs/api/feature-sample-todos/src/lib/sample-todos/sample-todos.module.ts
Add MongooseModule. Add SampleMongooseTableRepository to providers. Copy import { Module } from '@nestjs/common' ;
import { MongooseModule } from '@nestjs/mongoose' ;
import { SampleTodo } from './models/sample-todo.model' ;
import { SampleTodoSchema } from './models/sample-todo.schema' ;
import { SampleTodosRepository } from './sample-todos.repository' ;
import { SampleTodosResolver } from './sample-todos.resolver' ;
import { SampleTodosService } from './sample-todos.service' ;
@ Module ({
imports: [
MongooseModule. forFeature ([
{
name: SampleTodo.name,
schema: SampleTodoSchema
}
])
],
providers: [SampleTodosResolver, SampleTodosService, SampleTodosRepository]
})
export class SampleTodosModule {}
Updating app.module.tsUpdate the app.module.ts file in the apps/api/src/app/ directory:
Add DatabaseModule. Add SampleTodosModule. Copy @ Module ({
imports: [
ServeStaticModule. forRoot ({
rootPath: join (__dirname, '..' , 'web/.next' ),
exclude: [ '/api/*' , '/api/graphql' ]
}),
GraphQLModule. forRoot < ApolloDriverConfig >({
driver: ApolloDriver,
path: '/api/graphql' ,
autoSchemaFile: true
}),
// ---- Graphql ---- //
// When using Mongoose, the DatabaseModule is required.
DatabaseModule,
SampleTodosModule
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {}
Stay tuned for the next part as we continue configuring and integrating Nx, Nest.js, MongoDB, and Mongoose for a robust development experience.
Setting Up Nx Nestjs Mongodb Mongoose Integration - Part 3