Skip to content

ICrudify

Mite Gjorgjiev edited this page Oct 29, 2025 · 2 revisions

The ICrudify Interface

The ICrudify interface defines the configuration for the @Crudify decorator. It consists of two main sections: model and routes. Below is a detailed explanation of each property:

1. The model Section

This section defines the Mongoose model and its associated DTOs (Data Transfer Objects).

  • type: Type:
    The type of the Mongoose model. This is required and represents the model class (e.g., User).

  • cdto?: Type:
    The DTO for creating a new record (Create DTO). If not specified, the model itself will be used.

  • udto?: Type:
    The DTO for updating an existing record (Update DTO). If not specified, the model itself will be used.

  • schema?: Schema:
    The Mongoose schema associated with the model. If not specified, the default schema of the model will be used.


2. The routes Section

This section allows you to customize the automatically generated routes.

  • config?: Partial<Record<ControllerMethods, IRouteConfig>>:
    Specific configuration for each route. You can customize individual routes (e.g., findAll, create, update) using the IRouteConfig interface.
    Example:

    config: {
      findAll: {
        decorators: [UseGuards(AuthGuard)], // Add custom decorators
        disabled: false, // Enable or disable the route
      },
    },
  • exclude?: ControllerMethods[]:
    An array of methods to exclude from automatic route generation.
    Example:

    exclude: ['updateBulk', 'deleteBulk'], // Exclude bulk routes
  • decorators?: MethodDecorator[]:
    An array of decorators to apply to all routes globally. These decorators will be applied to every route generated by @Crudify.
    Example:

    decorators: [UseGuards(AuthGuard), ApiBearerAuth()], // Apply guards and Swagger docs globally

Overriding Methods

You can override the default CRUD methods provided by Crudify to add custom logic. Here’s how:

1. Override in the Service

Extend the CrudifyService and override its methods. For example, to customize the findAll method:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from './user.entity';
import { CrudifyService } from 'ncrudify';

@Injectable()
export class UserService extends CrudifyService<User> {
  constructor(@InjectModel(User.name) private userModel: Model<User>) {
    super(userModel);
  }

  // Override the default `findAll` method
  async findAll(query: any = {}): Promise<User[]> {
    // Add custom logic here
    return this.userModel.find(query).sort({ createdAt: -1 }).exec(); // Sort by creation date
  }
}

2. Override in the Controller

Use the @Crudify decorator and override specific methods in the controller. For example, to customize the findAll route:

import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
import { Crudify, CrudifyController } from 'ncrudify';

@Crudify({
  model: {
    type: User, // Define your model
  },
  routes: {
    exclude: ['updateBulk'], // Exclude specific routes
  },
})
@Controller('users')
export class UserController extends CrudifyController<User> {
  constructor(public service: UserService) {
    super(service);
  }

  // Override the default `findAll` method
  @Get()
  findAll() {
    return this.service.findAll();
  }
}

Example Configuration

Here’s a complete example of how to configure the @Crudify decorator:

import { UseGuards } from '@nestjs/common';
import { ApiBearerAuth } from '@nestjs/swagger';
import { AuthGuard } from './auth.guard';
import { User } from './user.entity';
import { CreateUserDto, UpdateUserDto } from './user.dto';

@Crudify({
  model: {
    type: User, // Mongoose model
    cdto: CreateUserDto, // Create DTO
    udto: UpdateUserDto, // Update DTO
  },
  routes: {
    config: {
      findAll: {
        decorators: [UseGuards(AuthGuard)], // Add a guard to the `findAll` route
        disabled: false, // Enable the route
      },
      create: {
        decorators: [ApiBearerAuth()], // Add Swagger documentation to the `create` route
      },
    },
    exclude: ['updateBulk'], // Exclude the `updateBulk` route
    decorators: [UseGuards(AuthGuard)], // Apply a guard to all routes
  },
})
@Controller('users')
export class UserController extends CrudifyController<User> {
  constructor(public service: UserService) {
    super(service);
  }
}

Key Points

  1. model: Defines the Mongoose model and DTOs for creating and updating records.
  2. routes: Allows you to customize or exclude specific routes and apply global decorators.
  3. Method Overriding: You can override default methods in the service or controller to add custom logic.
  4. Flexibility: The @Crudify decorator is highly configurable, making it easy to adapt to your specific needs.

This guide provides a comprehensive explanation of the ICrudify interface, how to configure the @Crudify decorator, and how to override methods. Let me know if you need further clarification or examples! 🚀

Authorization in NestJS with UseGuards and Custom Guards

Authorization is a critical aspect of any application to ensure that only authorized users can access specific routes or perform certain actions. In NestJS, you can manage route access control using decorators like UseGuards or by creating custom guards. Below, I’ll explain how to use UseGuards for authorization and how to create custom guards for more advanced use cases.


Using UseGuards for Authorization

The UseGuards decorator is a built-in NestJS feature that allows you to apply guards to routes. Guards implement the CanActivate interface and determine whether a request is authorized. You can use UseGuards in combination with the @Crudify decorator to protect your routes.

Example: Protecting Routes with UseGuards

import { UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
import { Crudify } from 'ncrudify';
import { User } from './user.entity';

@Crudify({
  model: {
    type: User, // Define your Mongoose model
  },
  routes: {
    config: {
      create: {
        decorators: [
          UseGuards(AuthGuard), // Protect the 'create' route with an authorization guard
        ],
      },
      findAll: {
        decorators: [
          UseGuards(AuthGuard), // Protect the 'findAll' route
        ],
      },
    },
  },
})
@Controller('users')
export class UserController {
  // Your controller methods
}

Explanation

  • UseGuards(AuthGuard): This applies the AuthGuard to the specified route. The guard will check if the request is authorized before allowing access.
  • Custom Logic: You can replace AuthGuard with any custom guard that implements your specific authorization logic (e.g., checking user roles or permissions).

Creating a Custom Authorization Guard

Custom guards allow you to implement complex authorization rules. For example, you might want to restrict access to certain routes based on user roles (e.g., only admins can create or delete records).

Example: Custom Guard for Role-Based Authorization

import { Injectable, 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();
    const user = request.user; // Assuming the user is attached to the request

    // Authorization logic: Check if the user has the 'admin' role
    if (user && user.role === 'admin') {
      return true; // Allow access
    }

    return false; // Deny access
  }
}

Explanation

  • canActivate Method: This method is called for every request. It determines whether the request is authorized.
  • ExecutionContext: Provides access to the request and response objects.
  • Role-Based Logic: In this example, the guard checks if the user has the admin role. If the user is an admin, access is granted; otherwise, it is denied.

Applying Custom Guards to Routes

Once you’ve created a custom guard, you can apply it to specific routes or globally to all routes.

Example: Applying a Custom Guard to Specific Routes

@Crudify({
  model: {
    type: User,
  },
  routes: {
    config: {
      create: {
        decorators: [
          UseGuards(AuthGuard), // Protect the 'create' route
        ],
      },
      delete: {
        decorators: [
          UseGuards(AuthGuard), // Protect the 'delete' route
        ],
      },
    },
  },
})
@Controller('users')
export class UserController {
  // Your controller methods
}

Example: Applying a Custom Guard Globally

@Crudify({
  model: {
    type: User,
  },
  routes: {
    decorators: [
      UseGuards(AuthGuard), // Apply the guard to all routes
    ],
  },
})
@Controller('users')
export class UserController {
  // Your controller methods
}

Advanced Use Cases

1. Combining Multiple Guards

You can combine multiple guards to enforce complex authorization rules. For example, you might want to check both authentication and role-based access.

@Crudify({
  model: {
    type: User,
  },
  routes: {
    config: {
      create: {
        decorators: [
          UseGuards(AuthGuard, AdminGuard), // Combine multiple guards
        ],
      },
    },
  },
})
@Controller('users')
export class UserController {
  // Your controller methods
}

2. Custom Decorators for Fine-Grained Control

You can create custom decorators to enforce fine-grained access control. For example, a @Roles decorator can restrict access based on user roles.

import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

Then, use this decorator in combination with a guard:

@Crudify({
  model: {
    type: User,
  },
  routes: {
    config: {
      create: {
        decorators: [
          Roles('admin'), // Apply the custom decorator
          UseGuards(AuthGuard, RolesGuard), // Use a guard to enforce the roles
        ],
      },
    },
  },
})
@Controller('users')
export class UserController {
  // Your controller methods
}

Key Takeaways

  1. UseGuards: A built-in decorator to apply guards for route protection.
  2. Custom Guards: Implement the CanActivate interface to create guards with custom authorization logic.
  3. Role-Based Access: Use guards to enforce role-based or permission-based access control.
  4. Global vs. Route-Specific Guards: Apply guards globally or to specific routes depending on your needs.
  5. Combining Guards: Combine multiple guards for complex authorization scenarios.

By leveraging UseGuards and custom guards, you can build a robust authorization system in your NestJS application. Let me know if you need further clarification or examples! 🚀

Clone this wiki locally