ncrudify
Version:
Configurable CRUD module for NestJS and Mongoose.
369 lines (302 loc) • 11.8 kB
Markdown
## **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:
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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`**
```typescript
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**
```typescript
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**
```typescript
@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**
```typescript
@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.
```typescript
@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.
```typescript
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
```
Then, use this decorator in combination with a guard:
```typescript
@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! 🚀