Are you a Mongoose lover but tired of writing the same CRUD code over and over again? Let Crudify do the heavy lifting for you! With this simple yet powerful NestJS library, you can instantly generate RESTful CRUD endpoints for your Mongoose models with just a few lines of code. Spend less time on boilerplate and more time building your application!
- Tired of repetitive code? Crudify automatically generates the full set of CRUD operations for your Mongoose models. No more writing the same functions every time you need a new endpoint.
- Swagger documentation out of the box! Crudify automatically generates a fully functional Swagger UI for your CRUD endpoints, making it easier for you and your team to test and document the API.
- Highly customizable? Of course! You can extend and tweak the generated endpoints to meet your specific needs.
- Error handling made easy: With an integrated logger, Crudify intercepts uncaught errors and makes debugging a breeze.
- Designed for NestJS: Seamlessly integrates with your NestJS project, no friction, no fuss.
Ready to get started? Just install Crudify and let it work its magic:
npm install ncrudify
- No more boilerplate: Generate Create, Read, Update, and Delete operations automatically.
- Swagger docs created for you: Just like that, Swagger UI will be set up automatically to interact with your API, making testing and documentation seamless.
- Custom error handling: Built-in error handling with easy logging configuration.
- Flexible: Customize the generated code or add your business logic.
- NestJS integration: Perfectly fits into your existing NestJS projects.
Once you set it up, Crudify will handle these endpoints for you:
POST /your-model:
Create a new recordPOST /your-model/bulk:
Create multiple new recordsGET /your-model:
Retrieve all recordsGET /your-model/:id:
Retrieve a specific record by IDPATCH /your-model/:id:
Update a record by IDPATCH /your-model/bulk
Update multiple records with filter in bodyPUT /your-model/:id:
Replace a record by IDDELETE /your-model/:id:
Delete a record by IDDELETE /your-model/bulk
Delete multiple records
If you’re done wasting time with repetitive CRUD code and ready to level up your NestJS game, Crudify is here for you. Let’s get started! 🚀
If you haven't already configured MongoDB in your NestJS project, follow these steps:
- Install Mongoose:
First, you need to install the@nestjs/mongoose
package andmongoose
to connect to your MongoDB database:
npm install @nestjs/mongoose mongoose
- Configure MongoDB in your NestJS module:
In your main application module (usually
app.module.ts
), import and configure theMongooseModule
to connect to your MongoDB instance:
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import { UserModule } from "./user/user.module";
@Module({
imports: [MongooseModule.forRoot(process.env.MONGODB_URI), UserModule],
})
export class AppModule {}
Crudify
makes it easy to generate Swagger documentation automatically for your API. Just a few steps and you're good to go!
- Import CrudifySwaggerModule in your AppModule:
In your
app.module.ts
, importCrudifySwaggerModule
fromncrudify
:
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import { UserModule } from "./user/user.module";
import { CrudifySwaggerModule } from "ncrudify";
@Module({
imports: [
MongooseModule.forRoot(process.env.MONGODB_URI),
UserModule,
CrudifySwaggerModule, // Import this module
],
})
export class AppModule {}
- Set up Swagger in your main.ts:
Finally, configure Swagger in your
main.ts
by usingCrudifySwaggerModule.setupSwagger()
:
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { CrudifySwaggerModule } from "ncrudify";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Setup Swagger
CrudifySwaggerModule.setupSwagger(app);
await app.listen(3000);
}
bootstrap();
This will automatically set up Swagger and generate a UI where you can interact with your API endpoints.
Crudify
uses errsole logger that captures uncaught errors and logs them. This logger is designed to make your debugging process smoother and more efficient. By using CrudifyLoggerModule
, you can quickly track any issues that arise in your application.
- Import
CrudifyLoggerModule
in yourAppModule
: First, you need to importCrudifyLoggerModule
in yourapp.module.ts
to enable logging for the application:
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import { UserModule } from "./user/user.module";
import { CrudifySwaggerModule } from "ncrudify";
@Module({
imports: [
MongooseModule.forRoot(process.env.MONGODB_URI),
UserModule,
CrudifySwaggerModule,
CrudifyLoggerModule.forRoot({
uri: process.env.MONGODB_URI,
dbName: process.env.MONGODB_LOGDB,
}), // Import this module
],
})
export class AppModule {}
This will set up the Crudify Logger to listen on port 3001 by default. The logger will catch any uncaught errors in your application and log them in a clear and organized way for you to review.
You can also adjust the logger’s configuration, such as the log level, to suit your project’s needs.
- Define your Mongoose model:
In this example, we define a
User
model using@nestjs/mongoose
decorators and@nestjs/swagger
for automatic API documentation.
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { ApiProperty } from "@nestjs/swagger";
import { Document, model, Model } from "mongoose";
@Schema({ timestamps: true })
export class User extends Document {
@ApiProperty({ example: "John Doe", description: "The name of the user" })
@Prop({ required: true })
name: string;
@ApiProperty({
example: "test@gmail.com",
description: "The email of the user",
})
@Prop({ required: true, unique: true })
email: string;
@ApiProperty({ example: 1, description: "The age of the user" })
@Prop({ required: false })
age: number;
}
export const UserSchema = SchemaFactory.createForClass(User);
export const UserModel: Model<User> = model < User > ("User", UserSchema);
- Create your service:
Extend
CrudifyService
to benefit from the automatically generated CRUD methods. Override any methods if needed for custom logic, such as thefindAll
method in this example.
import { Injectable } from '@nestjs/common';
import { User } from './entities/user.entity';
import { FilterQuery, Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { CrudifyService } from 'ncrudify';
@Injectable()
export class UserService extends CrudifyService<User> {
constructor(@InjectModel(User.name) protected userModel: Model<User>) {
super(userModel);
}
// override default method
async findAll(query: FilterQuery<any> = {}): Promise<any> {
const results = await this.userModel.find().exec();
return results;
}
}
- Create your controller:
Use the
@Crudify
decorator to automatically generate CRUD routes for theUser
model. You can still override specific methods likefindAll
as shown below.
import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './entities/user.entity';
import { Crudify, CrudifyController } from 'ncrudify';
@Crudify({
model: {
type: User,
},
routes: {
exclued: ['updateBulk'] // this route will not be generated
}
})
@Controller('users')
export class UserController extends CrudifyController<User> {
constructor(public service: UserService) {
super(service);
}
// override default method
@Get()
findAll() {
return this.service.findAll();
}
}
- Create your module:
Set up the module to import
MongooseModule
and register theUser
schema, while also providing the service and controller.
import { Module } from "@nestjs/common";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";
import { MongooseModule } from "@nestjs/mongoose";
import { UserSchema } from "./entities/user.entity";
@Module({
imports: [MongooseModule.forFeature([{ name: "User", schema: UserSchema }])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
In Crudify, you can add custom decorators to individual routes or apply a decorator to all routes in your controller.
To add custom decorators to specific routes, you can define them in the config object for each route.
- You can also apply decorators globally to all routes at once, rather than specifying them for each individual route. This is useful when you want to apply the same logic (like guards or documentation) across multiple endpoints in your controller. Here's an example:
@Crudify({
model: {
type: User, // Define your model
},
routes: {
config: {
// Custom decorators for the 'findAll' route
findAll: {
decorators: [
ApiOperation({
summary: 'Retrieve all users',
description: 'This endpoint fetches all users from the database.',
}),
],
disabled: false, // Optional: Specify whether this route is enabled or disabled
},
// Custom decorators for the 'create' route
create: {
decorators: [
ApiOperation({
summary: 'Create a new user',
description: 'Use this endpoint to create a new user.',
}),
],
},
},
decorators: [
UseGuards(LoggingGuard), // Apply a guard to all routes
ApiOperation({
summary: 'Global Operation Summary',
description: 'This is a global description that will apply to all routes.',
}), // Global Swagger documentation
],
},
})
class UserController {
// Your controller methods
}
-
Global Decorators: The decorators array inside the routes object will apply to all routes in the controller. For example, in the above example, UseGuards(LoggingGuard) and the global ApiOperation decorator are applied to every route in the controller.
-
Route-Specific Decorators: You can still apply specific decorators to individual routes via the config object. These will be combined with the global decorators.
For managing route access control, you can use authorization decorators such as UseGuards
or create your own custom decorators. These decorators help ensure that only authorized users can access specific routes.
UseGuards
is a built-in NestJS decorator that allows you to specify guards that control access to your routes. Guards implement the CanActivate
interface and determine if a request is authorized.
You can use UseGuards
in combination with Crudify to protect your routes with custom authorization logic.
@Crudify({
model: {
type: User,
},
routes: {
config: {
create: {
decorators: [
UseGuards(AuthGuard), // Protect 'create' route with an authorization guard
],
},
},
},
})
class UserController {
// Your controller methods
}
- Using
UseGuards
: In the example above,UseGuards(DisableRouteGuard)
is used to protect thefindAll
route. You can replaceDisableRouteGuard
with any custom guard that contains your authorization logic. - Custom Authorization Guards: Guards can be custom-built to implement complex authorization rules (e.g., checking user roles or permissions).
import { Injectable } from '@nestjs/common';
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
// Authorization logic: check if user has 'admin' role
return request.user && request.user.role === 'admin';
}
}
A flexible query parser that transforms URL query parameters into MongoDB queries. It supports filtering, sorting, pagination, field population, and complex logical operations.
The parser converts URL query parameters into a MongoDB-compatible query object with the following structure:
{
filters: Record<string, any>; // MongoDB query conditions
populate: any[]; // Population configurations
sort: Record<string, 1 | -1>; // Sort configurations
skip: number; // Number of documents to skip
limit: number; // Maximum number of documents to return
}
The parser supports the following filter operators:
Operator | Description | MongoDB Equivalent | Example |
---|---|---|---|
eq | Equals | $eq | name[eq]=John |
ne | Not equals | $ne | age[ne]=25 |
gt | Greater than | $gt | age[gt]=18 |
gte | Greater than or equals | $gte | age[gte]=18 |
lt | Less than | $lt | price[lt]=100 |
lte | Less than or equals | $lte | price[lte]=100 |
starts | Starts with (case insensitive) | $regex | email[starts]=john |
ends | Ends with (case insensitive) | $regex | email[ends]=gmail.com |
cont | Contains (case insensitive) | $regex | name[cont]=oh |
excl | Excludes (case insensitive) | $not | name[excl]=test |
in | In array | $in | status[in]=active,pending |
notin | Not in array | $nin | status[notin]=deleted,archived |
isnull | Field is null/undefined | $exists: false | deletedAt[isnull]=true |
notnull | Field is not null/undefined | $exists: true | email[notnull]=true |
between | Value is between (inclusive) | $gte, $lte | age[between]=18,25 |
# Find users aged 18 or older
/users?age[gte]=18
# Find users with gmail addresses
/users?email[ends]=gmail.com
# Find users with specific statuses
/users?status[in]=active,pending
# Find users aged between 18 and 25
/users?age[between]=18,25
You can combine multiple conditions on the same field using logical operators (AND/OR).
Add _op=and
or _op=or
to specify the logical operator for a field's conditions.
- Default behavior (without _op) is OR
- Use
fieldname_op=and
for AND operations - Use
fieldname_op=or
for OR operations (explicit)
# Emails that start with 'john' AND end with 'gmail.com'
/users?email[starts]=john&email[ends]=gmail.com
# Emails that start with 'john' OR end with 'gmail.com'
/users?email[starts]=john&email[ends]=gmail.com&email_op=or
# Mix AND/OR operations on different fields
/users?email[starts]=john&email[ends]=gmail.com&email_op=and&status[in]=active,pending&status_op=or
Sort results by one or more fields.
- Use
sort=field
for ascending order - Use
sort=-field
for descending order - Combine multiple fields with commas
# Sort by name ascending
/users?sort=name
# Sort by created date descending
/users?sort=-createdAt
# Sort by status ascending, then created date descending
/users?sort=status,-createdAt
Control the number of results and their offset.
skip
: Number of documents to skiplimit
: Maximum number of documents to return
# Get the first 10 results
/users?limit=10
# Get 10 results, starting from the 20th document
/users?skip=20&limit=10
Populate references to other collections with various options for controlling the populated data.
# Populate a single reference
/users?populate=posts
# Populate multiple references
/users?populate=posts,comments
# Populate nested references
/users?populate=posts.author,posts.comments
Choose which fields to include in populated documents:
/users?populate.posts.select=title,content,author
Apply filters to populated documents:
/users?populate.posts.status[eq]=published
Sort populated documents:
/users?populate.posts.sort=-createdAt
/users?populate.posts.select=title,content&populate.posts.status[eq]=published&populate.posts.sort=-createdAt
Here are some complete examples combining multiple features:
/users?
name[starts]=john&
email[ends]=gmail.com&
email_op=and&
age[between]=25,35&
status[in]=active,premium&
sort=-createdAt&
limit=10&
populate=posts,comments
This query will:
- Find users whose name starts with "john"
- AND whose email ends with "gmail.com"
- AND whose age is between 25 and 35
- AND whose status is either active or premium
- Sort results by creation date (newest first)
- Limit to 10 results
- Populate posts and comments
/posts?
title[cont]=javascript&
status[eq]=published&
tags[in]=tutorial,nodejs&
populate.author.select=name,email&
populate.comments.status[eq]=approved&
populate.comments.sort=-createdAt&
sort=-views,title&
skip=20&
limit=10
This query will:
- Find posts containing "javascript" in the title
- That are published
- Tagged with either "tutorial" or "nodejs"
- Populate author (only name and email)
- Populate approved comments (sorted by creation date)
- Sort results by views (descending) and title (ascending)
- Skip first 20 results
- Limit to 10 results
We love contributions! Whether you’ve spotted a bug or have an awesome idea, feel free to open an issue or submit a PR.
If you like my work and want to support give a â? to the repo.
Thank you so much ��.
See the LICENSE file for more details.