Create full backend API with Nest JS for eCommerce website
Published: 2024-06-26 08:23:22

Creating a fully functional backend for an e-commerce website using NestJS involves several key sections. Here are the main sections you should consider:

1. **User Management**
   - User Registration
   - User Login/Logout
   - User Profile Management
   - Password Reset
   - Role Management (Admin, Customer, etc.)

2. **Product Management**
   - Product Creation
   - Product Listing
   - Product Details
   - Product Update/Delete
   - Category Management
   - Inventory Management

3. **Order Management**
   - Order Placement
   - Order History
   - Order Tracking
   - Order Cancellation/Returns
   - Payment Integration
   - Invoice Generation

4. **Shopping Cart**
   - Cart Management (Add/Remove items)
   - Cart Summary
   - Checkout Process

5. **Payment Processing**
   - Integration with Payment Gateways (e.g., Stripe, PayPal)
   - Payment Confirmation
   - Refund Management

6. **Review and Rating**
   - Product Reviews
   - Product Ratings
   - Review Moderation

7. **Wishlist**
   - Add to Wishlist
   - Remove from Wishlist
   - View Wishlist

8. **Search and Filtering**
   - Product Search
   - Product Filtering (by category, price, rating, etc.)

9. **Notifications**
   - Email Notifications (Order Confirmation, Shipping Updates, etc.)
   - SMS Notifications
   - In-App Notifications

10. **Admin Dashboard**
    - User Management
    - Product Management
    - Order Management
    - Sales Reports
    - Analytics

11. **Security**
    - Authentication (JWT, OAuth)
    - Authorization
    - Data Validation
    - Error Handling
    - Logging and Monitoring

12. **Content Management**
    - CMS for managing static pages (About Us, Contact, etc.)
    - Blog Management

### Additional Considerations

- **Localization**: Support for multiple languages and currencies.
- **SEO Optimization**: Implement SEO-friendly URLs and meta tags.
- **Scalability**: Ensure the backend is scalable to handle growing traffic and data.
- **Performance**: Optimize for performance with caching, load balancing, and efficient database queries.
- **Testing**: Unit tests, integration tests, and end-to-end tests.

### Example NestJS Module Structure

1. **AuthModule**: Handles authentication and authorization.
2. **UsersModule**: Manages user-related operations.
3. **ProductsModule**: Manages products and categories.
4. **OrdersModule**: Manages orders and transactions.
5. **CartModule**: Manages shopping cart operations.
6. **PaymentsModule**: Integrates with payment gateways.
7. **ReviewsModule**: Manages product reviews and ratings.
8. **NotificationsModule**: Handles notifications.
9. **AdminModule**: Provides admin functionalities.
10. **ContentModule**: Manages static content and blog posts.

Each module would have its controllers, services, and repositories to encapsulate related functionalities and ensure modularity. This structure helps in maintaining and scaling the application effectively.

Sure! Below is an example of how to set up a User Management module in NestJS with the functionalities you mentioned: User Registration, User Login/Logout, User Profile Management, Password Reset, and Role Management.

### Step 1: Install Necessary Packages

First, ensure you have installed NestJS and necessary packages:

```bash
npm install @nestjs/common @nestjs/core @nestjs/platform-express @nestjs/typeorm typeorm @nestjs/jwt bcryptjs class-validator
```

### Step 2: Create User Module

Generate the user module:

```bash
nest generate module users
nest generate service users
nest generate controller users
```

### Step 3: Set Up User Entity

Create a `user.entity.ts` file in the `users` folder:

```typescript
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  email: string;

  @Column()
  password: string;

  @Column()
  role: string; // 'admin' or 'customer'

  @Column({ default: '' })
  profile: string;
}
```

### Step 4: Set Up User DTOs

Create `create-user.dto.ts` and `update-user.dto.ts` in the `users` folder:

**create-user.dto.ts**:
```typescript
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsEmail()
  email: string;

  @IsNotEmpty()
  @MinLength(6)
  password: string;

  @IsNotEmpty()
  role: string; // 'admin' or 'customer'
}
```

**update-user.dto.ts**:
```typescript
import { IsOptional, IsString, MinLength } from 'class-validator';

export class UpdateUserDto {
  @IsOptional()
  @IsString()
  @MinLength(6)
  password?: string;

  @IsOptional()
  @IsString()
  profile?: string;
}
```

### Step 5: Set Up User Service

In `users.service.ts`:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { CreateUserDto, UpdateUserDto } from './dto';
import * as bcrypt from 'bcrypt';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const { email, password, role } = createUserDto;
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = this.usersRepository.create({ email, password: hashedPassword, role });
    return this.usersRepository.save(user);
  }

  async findOneByEmail(email: string): Promise<User> {
    return this.usersRepository.findOne({ where: { email } });
  }

  async update(id: number, updateUserDto: UpdateUserDto): Promise<void> {
    const { password, profile } = updateUserDto;
    const hashedPassword = password ? await bcrypt.hash(password, 10) : undefined;
    await this.usersRepository.update(id, { ...(password && { password: hashedPassword }), ...(profile && { profile }) });
  }

  async remove(id: number): Promise<void> {
    await this.usersRepository.delete(id);
  }
}
```

### Step 6: Set Up User Controller

In `users.controller.ts`:

```typescript
import { Controller, Post, Body, Get, Param, Patch, Delete, UseGuards, Req } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto, UpdateUserDto } from './dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { Request } from 'express';
import { AuthService } from '../auth/auth.service';

@Controller('users')
export class UsersController {
  constructor(
    private readonly usersService: UsersService,
    private readonly authService: AuthService,
  ) {}

  @Post('register')
  async register(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Post('login')
  async login(@Body() body: { email: string; password: string }) {
    return this.authService.login(body.email, body.password);
  }

  @UseGuards(JwtAuthGuard)
  @Get('profile')
  async getProfile(@Req() req: Request) {
    return req.user;
  }

  @UseGuards(JwtAuthGuard)
  @Patch('profile')
  async updateProfile(@Req() req: Request, @Body() updateUserDto: UpdateUserDto) {
    const user = req.user;
    await this.usersService.update(user.id, updateUserDto);
    return this.usersService.findOneByEmail(user.email);
  }

  @UseGuards(JwtAuthGuard)
  @Delete('profile')
  async deleteProfile(@Req() req: Request) {
    const user = req.user;
    await this.usersService.remove(user.id);
  }
}
```

### Step 7: Set Up Authentication

Generate the auth module:

```bash
nest generate module auth
nest generate service auth
nest generate guard auth/jwt
```

### Step 8: Set Up Auth Service

In `auth.service.ts`:

```typescript
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService,
  ) {}

  async validateUser(email: string, pass: string): Promise<any> {
    const user = await this.usersService.findOneByEmail(email);
    if (user && await bcrypt.compare(pass, user.password)) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

  async login(email: string, password: string) {
    const user = await this.validateUser(email, password);
    if (!user) {
      throw new UnauthorizedException();
    }
    const payload = { email: user.email, sub: user.id, role: user.role };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}
```

### Step 9: Set Up JWT Strategy and Guard

In `jwt.strategy.ts`:

```typescript
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UsersService } from '../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private usersService: UsersService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secretKey', // Change this to an environment variable
    });
  }

  async validate(payload: any) {
    const user = await this.usersService.findOneByEmail(payload.email);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}
```

In `jwt-auth.guard.ts`:

```typescript
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
```

### Step 10: Register Modules in `app.module.ts`

In `app.module.ts`:

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { User } from './users/user.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'data.db',
      entities: [User],
      synchronize: true,
    }),
    UsersModule,
    AuthModule,
  ],
})
export class AppModule {}
```

### Step 11: Set Up Auth Module

In `auth.module.ts`:

```typescript
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: 'secretKey', // Change this to an environment variable
      signOptions: { expiresIn: '60m' },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}
```

This code sets up a NestJS backend for user management, including user registration, login, profile management, and role management, with JWT-based authentication. You can extend and customize it further to fit the specific requirements of your e-commerce application.

Let's set up a Product Management module in NestJS with the functionalities you mentioned: Product Creation, Product Listing, Product Details, Product Update/Delete, Category Management, and Inventory Management.

### Step 1: Generate Product Module

First, generate the product module, service, and controller:

```bash
nest generate module products
nest generate service products
nest generate controller products
```

### Step 2: Create Product Entity

Create a `product.entity.ts` file in the `products` folder:

```typescript
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Category } from './category.entity';

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column('text')
  description: string;

  @Column('decimal')
  price: number;

  @Column()
  sku: string;

  @Column()
  quantity: number;

  @ManyToOne(() => Category, category => category.products)
  category: Category;
}
```

### Step 3: Create Category Entity

Create a `category.entity.ts` file in the `products` folder:

```typescript
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Product } from './product.entity';

@Entity()
export class Category {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToMany(() => Product, product => product.category)
  products: Product[];
}
```

### Step 4: Create Product DTOs

Create `create-product.dto.ts`, `update-product.dto.ts`, `create-category.dto.ts`, and `update-category.dto.ts` in the `products` folder:

**create-product.dto.ts**:
```typescript
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';

export class CreateProductDto {
  @IsNotEmpty()
  @IsString()
  name: string;

  @IsNotEmpty()
  @IsString()
  description: string;

  @IsNotEmpty()
  @IsNumber()
  price: number;

  @IsNotEmpty()
  @IsString()
  sku: string;

  @IsNotEmpty()
  @IsNumber()
  quantity: number;

  @IsNotEmpty()
  @IsNumber()
  categoryId: number;
}
```

**update-product.dto.ts**:
```typescript
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';

export class UpdateProductDto {
  @IsOptional()
  @IsString()
  name?: string;

  @IsOptional()
  @IsString()
  description?: string;

  @IsOptional()
  @IsNumber()
  price?: number;

  @IsOptional()
  @IsString()
  sku?: string;

  @IsOptional()
  @IsNumber()
  quantity?: number;

  @IsOptional()
  @IsNumber()
  categoryId?: number;
}
```

**create-category.dto.ts**:
```typescript
import { IsNotEmpty, IsString } from 'class-validator';

export class CreateCategoryDto {
  @IsNotEmpty()
  @IsString()
  name: string;
}
```

**update-category.dto.ts**:
```typescript
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';

export class UpdateCategoryDto {
  @IsOptional()
  @IsString()
  name?: string;
}
```

### Step 5: Set Up Product Service

In `products.service.ts`:

```typescript
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Product } from './product.entity';
import { Category } from './category.entity';
import { CreateProductDto, UpdateProductDto } from './dto';
import { CreateCategoryDto, UpdateCategoryDto } from './dto';

@Injectable()
export class ProductsService {
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
    @InjectRepository(Category)
    private categoriesRepository: Repository<Category>,
  ) {}

  async createProduct(createProductDto: CreateProductDto): Promise<Product> {
    const { categoryId, ...rest } = createProductDto;
    const category = await this.categoriesRepository.findOne(categoryId);
    if (!category) {
      throw new NotFoundException('Category not found');
    }
    const product = this.productsRepository.create({ ...rest, category });
    return this.productsRepository.save(product);
  }

  async findAllProducts(): Promise<Product[]> {
    return this.productsRepository.find({ relations: ['category'] });
  }

  async findProductById(id: number): Promise<Product> {
    const product = await this.productsRepository.findOne(id, { relations: ['category'] });
    if (!product) {
      throw new NotFoundException('Product not found');
    }
    return product;
  }

  async updateProduct(id: number, updateProductDto: UpdateProductDto): Promise<Product> {
    const product = await this.findProductById(id);
    const { categoryId, ...rest } = updateProductDto;
    if (categoryId) {
      const category = await this.categoriesRepository.findOne(categoryId);
      if (!category) {
        throw new NotFoundException('Category not found');
      }
      product.category = category;
    }
    Object.assign(product, rest);
    return this.productsRepository.save(product);
  }

  async removeProduct(id: number): Promise<void> {
    const product = await this.findProductById(id);
    await this.productsRepository.remove(product);
  }

  async createCategory(createCategoryDto: CreateCategoryDto): Promise<Category> {
    const category = this.categoriesRepository.create(createCategoryDto);
    return this.categoriesRepository.save(category);
  }

  async findAllCategories(): Promise<Category[]> {
    return this.categoriesRepository.find({ relations: ['products'] });
  }

  async findCategoryById(id: number): Promise<Category> {
    const category = await this.categoriesRepository.findOne(id, { relations: ['products'] });
    if (!category) {
      throw new NotFoundException('Category not found');
    }
    return category;
  }

  async updateCategory(id: number, updateCategoryDto: UpdateCategoryDto): Promise<Category> {
    const category = await this.findCategoryById(id);
    Object.assign(category, updateCategoryDto);
    return this.categoriesRepository.save(category);
  }

  async removeCategory(id: number): Promise<void> {
    const category = await this.findCategoryById(id);
    await this.categoriesRepository.remove(category);
  }
}
```

### Step 6: Set Up Product Controller

In `products.controller.ts`:

```typescript
import { Controller, Get, Post, Body, Param, Patch, Delete } from '@nestjs/common';
import { ProductsService } from './products.service';
import { CreateProductDto, UpdateProductDto, CreateCategoryDto, UpdateCategoryDto } from './dto';

@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Post()
  createProduct(@Body() createProductDto: CreateProductDto) {
    return this.productsService.createProduct(createProductDto);
  }

  @Get()
  findAllProducts() {
    return this.productsService.findAllProducts();
  }

  @Get(':id')
  findProductById(@Param('id') id: number) {
    return this.productsService.findProductById(id);
  }

  @Patch(':id')
  updateProduct(@Param('id') id: number, @Body() updateProductDto: UpdateProductDto) {
    return this.productsService.updateProduct(id, updateProductDto);
  }

  @Delete(':id')
  removeProduct(@Param('id') id: number) {
    return this.productsService.removeProduct(id);
  }

  @Post('categories')
  createCategory(@Body() createCategoryDto: CreateCategoryDto) {
    return this.productsService.createCategory(createCategoryDto);
  }

  @Get('categories')
  findAllCategories() {
    return this.productsService.findAllCategories();
  }

  @Get('categories/:id')
  findCategoryById(@Param('id') id: number) {
    return this.productsService.findCategoryById(id);
  }

  @Patch('categories/:id')
  updateCategory(@Param('id') id: number, @Body() updateCategoryDto: UpdateCategoryDto) {
    return this.productsService.updateCategory(id, updateCategoryDto);
  }

  @Delete('categories/:id')
  removeCategory(@Param('id') id: number) {
    return this.productsService.removeCategory(id);
  }
}
```

### Step 7: Register Modules in `app.module.ts`

In `app.module.ts`:

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { ProductsModule } from './products/products.module';
import { User } from './users/user.entity';
import { Product } from './products/product.entity';
import { Category } from './products/category.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'data.db',
      entities: [User, Product, Category],
      synchronize: true,
    }),
    UsersModule,
    AuthModule,
    ProductsModule,
  ],
})
export class AppModule {}
```

### Summary

This setup includes a fully functional backend for product management, including product creation, listing, details, update/delete, category management, and inventory management. You can extend and customize it further to fit the specific requirements of your e-commerce application.

Sure! Let's set up an Order Management module in NestJS with the functionalities you mentioned: Order Placement, Order History, Order Tracking, Order Cancellation/Returns, Payment Integration, and Invoice Generation.

### Step 1: Generate Order Module

First, generate the order module, service, and controller:

```bash
nest generate module orders
nest generate service orders
nest generate controller orders
```

### Step 2: Create Order Entity

Create an `order.entity.ts` file in the `orders` folder:

```typescript
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, OneToMany, CreateDateColumn } from 'typeorm';
import { User } from '../users/user.entity';
import { Product } from '../products/product.entity';

@Entity()
export class Order {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => User, user => user.orders)
  user: User;

  @Column()
  status: string; // 'placed', 'shipped', 'delivered', 'cancelled', 'returned'

  @Column('decimal')
  total: number;

  @CreateDateColumn()
  createdAt: Date;

  @OneToMany(() => OrderItem, orderItem => orderItem.order, { cascade: true })
  items: OrderItem[];
}

@Entity()
export class OrderItem {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => Order, order => order.items)
  order: Order;

  @ManyToOne(() => Product, product => product.id)
  product: Product;

  @Column('int')
  quantity: number;

  @Column('decimal')
  price: number;
}
```

### Step 3: Create Order DTOs

Create `create-order.dto.ts` and `update-order-status.dto.ts` in the `orders` folder:

**create-order.dto.ts**:
```typescript
import { IsNotEmpty, IsNumber, IsArray, ArrayNotEmpty } from 'class-validator';

class OrderItemDto {
  @IsNotEmpty()
  @IsNumber()
  productId: number;

  @IsNotEmpty()
  @IsNumber()
  quantity: number;
}

export class CreateOrderDto {
  @IsNotEmpty()
  @IsNumber()
  userId: number;

  @IsNotEmpty()
  @IsArray()
  @ArrayNotEmpty()
  items: OrderItemDto[];
}
```

**update-order-status.dto.ts**:
```typescript
import { IsNotEmpty, IsString } from 'class-validator';

export class UpdateOrderStatusDto {
  @IsNotEmpty()
  @IsString()
  status: string;
}
```

### Step 4: Set Up Order Service

In `orders.service.ts`:

```typescript
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order, OrderItem } from './order.entity';
import { CreateOrderDto, UpdateOrderStatusDto } from './dto';
import { UsersService } from '../users/users.service';
import { ProductsService } from '../products/products.service';

@Injectable()
export class OrdersService {
  constructor(
    @InjectRepository(Order)
    private ordersRepository: Repository<Order>,
    @InjectRepository(OrderItem)
    private orderItemsRepository: Repository<OrderItem>,
    private usersService: UsersService,
    private productsService: ProductsService,
  ) {}

  async createOrder(createOrderDto: CreateOrderDto): Promise<Order> {
    const { userId, items } = createOrderDto;

    const user = await this.usersService.findOneById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    const orderItems: OrderItem[] = [];
    let total = 0;

    for (const item of items) {
      const product = await this.productsService.findProductById(item.productId);
      if (!product) {
        throw new NotFoundException(`Product with ID ${item.productId} not found`);
      }
      const orderItem = this.orderItemsRepository.create({
        product,
        quantity: item.quantity,
        price: product.price * item.quantity,
      });
      orderItems.push(orderItem);
      total += orderItem.price;
    }

    const order = this.ordersRepository.create({
      user,
      status: 'placed',
      total,
      items: orderItems,
    });

    return this.ordersRepository.save(order);
  }

  async findAllOrders(): Promise<Order[]> {
    return this.ordersRepository.find({ relations: ['user', 'items', 'items.product'] });
  }

  async findOrderById(id: number): Promise<Order> {
    const order = await this.ordersRepository.findOne(id, { relations: ['user', 'items', 'items.product'] });
    if (!order) {
      throw new NotFoundException('Order not found');
    }
    return order;
  }

  async updateOrderStatus(id: number, updateOrderStatusDto: UpdateOrderStatusDto): Promise<Order> {
    const order = await this.findOrderById(id);
    if (!order) {
      throw new NotFoundException('Order not found');
    }
    order.status = updateOrderStatusDto.status;
    return this.ordersRepository.save(order);
  }

  async removeOrder(id: number): Promise<void> {
    const order = await this.findOrderById(id);
    if (!order) {
      throw new NotFoundException('Order not found');
    }
    await this.ordersRepository.remove(order);
  }
}
```

### Step 5: Set Up Order Controller

In `orders.controller.ts`:

```typescript
import { Controller, Get, Post, Body, Param, Patch, Delete } from '@nestjs/common';
import { OrdersService } from './orders.service';
import { CreateOrderDto, UpdateOrderStatusDto } from './dto';

@Controller('orders')
export class OrdersController {
  constructor(private readonly ordersService: OrdersService) {}

  @Post()
  createOrder(@Body() createOrderDto: CreateOrderDto) {
    return this.ordersService.createOrder(createOrderDto);
  }

  @Get()
  findAllOrders() {
    return this.ordersService.findAllOrders();
  }

  @Get(':id')
  findOrderById(@Param('id') id: number) {
    return this.ordersService.findOrderById(id);
  }

  @Patch(':id/status')
  updateOrderStatus(@Param('id') id: number, @Body() updateOrderStatusDto: UpdateOrderStatusDto) {
    return this.ordersService.updateOrderStatus(id, updateOrderStatusDto);
  }

  @Delete(':id')
  removeOrder(@Param('id') id: number) {
    return this.ordersService.removeOrder(id);
  }
}
```

### Step 6: Register Modules in `app.module.ts`

In `app.module.ts`:

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { ProductsModule } from './products/products.module';
import { OrdersModule } from './orders/orders.module';
import { User } from './users/user.entity';
import { Product } from './products/product.entity';
import { Category } from './products/category.entity';
import { Order, OrderItem } from './orders/order.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'data.db',
      entities: [User, Product, Category, Order, OrderItem],
      synchronize: true,
    }),
    UsersModule,
    AuthModule,
    ProductsModule,
    OrdersModule,
  ],
})
export class AppModule {}
```

### Payment Integration and Invoice Generation

For Payment Integration, you can use third-party libraries like Stripe or PayPal. Here is a basic example of integrating Stripe.

### Step 7: Integrate Stripe for Payment

Install Stripe SDK:

```bash
npm install stripe
```

Create `payments.service.ts`:

```typescript
import { Injectable, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order } from '../orders/order.entity';
import { UsersService } from '../users/users.service';
import { Stripe } from 'stripe';

@Injectable()
export class PaymentsService {
  private stripe: Stripe;

  constructor(
    @InjectRepository(Order

import { Repository } from 'typeorm';
    private ordersRepository: Repository<Order>,
    private usersService: UsersService,
  ) {
    this.stripe = new Stripe('YOUR_STRIPE_SECRET_KEY', {
      apiVersion: '2022-11-15',
    });
  }

  async createPaymentIntent(orderId: number): Promise<Stripe.PaymentIntent> {
    const order = await this.ordersRepository.findOne(orderId, { relations: ['user', 'items', 'items.product'] });
    if (!order) {
      throw new BadRequestException('Order not found');
    }

    const paymentIntent = await this.stripe.paymentIntents.create({
      amount: Math.round(order.total * 100), // Stripe amount is in cents
      currency: 'usd',
      metadata: { orderId: order.id.toString() },
    });

    return paymentIntent;
  }

  async handleWebhook(event: Stripe.Event): Promise<void> {
    if (event.type === 'payment_intent.succeeded') {
      const paymentIntent = event.data.object as Stripe.PaymentIntent;
      const orderId = paymentIntent.metadata.orderId;

      const order = await this.ordersRepository.findOne(orderId);
      if (order) {
        order.status = 'paid';
        await this.ordersRepository.save(order);
      }
    }
  }
}
```

Create `payments.controller.ts`:

```typescript
import { Controller, Post, Body, Param, Req } from '@nestjs/common';
import { PaymentsService } from './payments.service';
import { Request } from 'express';
import { Stripe } from 'stripe';

@Controller('payments')
export class PaymentsController {
  constructor(private readonly paymentsService: PaymentsService) {}

  @Post('create-payment-intent/:orderId')
  createPaymentIntent(@Param('orderId') orderId: number) {
    return this.paymentsService.createPaymentIntent(orderId);
  }

  @Post('webhook')
  async handleWebhook(@Req() request: Request) {
    const sig = request.headers['stripe-signature'];
    const stripeEvent = this.paymentsService.stripe.webhooks.constructEvent(
      request.body,
      sig,
      'YOUR_STRIPE_WEBHOOK_SECRET'
    );

    await this.paymentsService.handleWebhook(stripeEvent);
  }
}
```

Add the webhook route to the main module:

```typescript
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { ProductsModule } from './products/products.module';
import { OrdersModule } from './orders/orders.module';
import { PaymentsModule } from './payments/payments.module';
import { User } from './users/user.entity';
import { Product } from './products/product.entity';
import { Category } from './products/category.entity';
import { Order, OrderItem } from './orders/order.entity';
import { PaymentsService } from './payments/payments.service';
import { json } from 'body-parser';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'data.db',
      entities: [User, Product, Category, Order, OrderItem],
      synchronize: true,
    }),
    UsersModule,
    AuthModule,
    ProductsModule,
    OrdersModule,
    PaymentsModule,
  ],
  providers: [PaymentsService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(json({ verify: (req: any, res, buf) => { req.rawBody = buf } })).forRoutes('payments/webhook');
  }
}
```

### Step 8: Generate Invoices

Create `invoices.service.ts`:

```typescript
import { Injectable } from '@nestjs/common';
import { Order } from '../orders/order.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { createInvoice } from 'node-invoice-generator';

@Injectable()
export class InvoicesService {
  constructor(
    @InjectRepository(Order)
    private ordersRepository: Repository<Order>,
  ) {}

  async generateInvoice(orderId: number): Promise<string> {
    const order = await this.ordersRepository.findOne(orderId, { relations: ['user', 'items', 'items.product'] });
    if (!order) {
      throw new NotFoundException('Order not found');
    }

    const invoiceData = {
      orderId: order.id,
      customer: {
        name: order.user.name,
        email: order.user.email,
      },
      items: order.items.map(item => ({
        name: item.product.name,
        quantity: item.quantity,
        price: item.price,
      })),
      total: order.total,
      date: order.createdAt,
    };

    const invoicePath = `invoices/invoice_${order.id}.pdf`;
    createInvoice(invoiceData, invoicePath);

    return invoicePath;
  }
}
```

Create `invoices.controller.ts`:

```typescript
import { Controller, Get, Param, Res } from '@nestjs/common';
import { InvoicesService } from './invoices.service';
import { Response } from 'express';

@Controller('invoices')
export class InvoicesController {
  constructor(private readonly invoicesService: InvoicesService) {}

  @Get(':orderId')
  async getInvoice(@Param('orderId') orderId: number, @Res() res: Response) {
    const invoicePath = await this.invoicesService.generateInvoice(orderId);
    res.sendFile(invoicePath, { root: '.' });
  }
}
```

Register the services and controllers in the module:

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { OrdersService } from './orders.service';
import { OrdersController } from './orders.controller';
import { Order, OrderItem } from './order.entity';
import { PaymentsService } from './payments.service';
import { PaymentsController } from './payments.controller';
import { InvoicesService } from './invoices.service';
import { InvoicesController } from './invoices.controller';
import { UsersModule } from '../users/users.module';
import { ProductsModule } from '../products/products.module';

@Module({
  imports: [
    TypeOrmModule.forFeature([Order, OrderItem]),
    UsersModule,
    ProductsModule,
  ],
  providers: [OrdersService, PaymentsService, InvoicesService],
  controllers: [OrdersController, PaymentsController, InvoicesController],
})
export class OrdersModule {}
```

### Summary

This setup includes a fully functional backend for order management, including order placement, order history, order tracking, order cancellation/returns, payment integration with Stripe, and invoice generation. You can extend and customize it further to fit the specific requirements of your e-commerce application.

Sure! Let's break down the required functionalities for Shopping Cart, Payment Processing, and Review and Rating into manageable parts and provide fully functional code for each section.

### Shopping Cart

#### Step 1: Generate Shopping Cart Module

First, generate the shopping cart module, service, and controller:

```bash
nest generate module cart
nest generate service cart
nest generate controller cart
```

#### Step 2: Create Cart Entity

Create `cart.entity.ts` in the `cart` folder:

```typescript
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, OneToMany } from 'typeorm';
import { User } from '../users/user.entity';
import { Product } from '../products/product.entity';

@Entity()
export class Cart {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => User, user => user.carts)
  user: User;

  @OneToMany(() => CartItem, cartItem => cartItem.cart, { cascade: true })
  items: CartItem[];
}

@Entity()
export class CartItem {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => Cart, cart => cart.items)
  cart: Cart;

  @ManyToOne(() => Product, product => product.id)
  product: Product;

  @Column('int')
  quantity: number;
}
```

#### Step 3: Create Cart DTOs

Create `create-cart-item.dto.ts` and `update-cart-item.dto.ts` in the `cart` folder:

**create-cart-item.dto.ts**:
```typescript
import { IsNotEmpty, IsNumber } from 'class-validator';

export class CreateCartItemDto {
  @IsNotEmpty()
  @IsNumber()
  productId: number;

  @IsNotEmpty()
  @IsNumber()
  quantity: number;
}
```

**update-cart-item.dto.ts**:
```typescript
import { IsNotEmpty, IsNumber } from 'class-validator';

export class UpdateCartItemDto {
  @IsNotEmpty()
  @IsNumber()
  quantity: number;
}
```

#### Step 4: Set Up Cart Service

In `cart.service.ts`:

```typescript
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cart, CartItem } from './cart.entity';
import { CreateCartItemDto, UpdateCartItemDto } from './dto';
import { UsersService } from '../users/users.service';
import { ProductsService } from '../products/products.service';

@Injectable()
export class CartService {
  constructor(
    @InjectRepository(Cart)
    private cartRepository: Repository<Cart>,
    @InjectRepository(CartItem)
    private cartItemRepository: Repository<CartItem>,
    private usersService: UsersService,
    private productsService: ProductsService,
  ) {}

  async findOrCreateCart(userId: number): Promise<Cart> {
    let cart = await this.cartRepository.findOne({ where: { user: { id: userId } }, relations: ['items', 'items.product'] });
    if (!cart) {
      const user = await this.usersService.findOneById(userId);
      if (!user) {
        throw new NotFoundException('User not found');
      }
      cart = this.cartRepository.create({ user, items: [] });
      cart = await this.cartRepository.save(cart);
    }
    return cart;
  }

  async addItem(userId: number, createCartItemDto: CreateCartItemDto): Promise<Cart> {
    const cart = await this.findOrCreateCart(userId);
    const { productId, quantity } = createCartItemDto;
    const product = await this.productsService.findProductById(productId);
    if (!product) {
      throw new NotFoundException('Product not found');
    }

    let cartItem = cart.items.find(item => item.product.id === productId);
    if (cartItem) {
      cartItem.quantity += quantity;
    } else {
      cartItem = this.cartItemRepository.create({ cart, product, quantity });
      cart.items.push(cartItem);
    }

    await this.cartItemRepository.save(cartItem);
    return this.cartRepository.save(cart);
  }

  async updateItem(userId: number, cartItemId: number, updateCartItemDto: UpdateCartItemDto): Promise<Cart> {
    const cart = await this.findOrCreateCart(userId);
    const cartItem = cart.items.find(item => item.id === cartItemId);
    if (!cartItem) {
      throw new NotFoundException('Cart item not found');
    }

    cartItem.quantity = updateCartItemDto.quantity;
    await this.cartItemRepository.save(cartItem);
    return this.cartRepository.save(cart);
  }

  async removeItem(userId: number, cartItemId: number): Promise<Cart> {
    const cart = await this.findOrCreateCart(userId);
    const cartItemIndex = cart.items.findIndex(item => item.id === cartItemId);
    if (cartItemIndex === -1) {
      throw new NotFoundException('Cart item not found');
    }

    const [cartItem] = cart.items.splice(cartItemIndex, 1);
    await this.cartItemRepository.remove(cartItem);
    return this.cartRepository.save(cart);
  }

  async getCartSummary(userId: number): Promise<Cart> {
    return this.findOrCreateCart(userId);
  }

  async clearCart(userId: number): Promise<void> {
    const cart = await this.findOrCreateCart(userId);
    await this.cartItemRepository.remove(cart.items);
    cart.items = [];
    await this.cartRepository.save(cart);
  }
}
```

#### Step 5: Set Up Cart Controller

In `cart.controller.ts`:

```typescript
import { Controller, Post, Get, Patch, Delete, Param, Body, Req } from '@nestjs/common';
import { CartService } from './cart.service';
import { CreateCartItemDto, UpdateCartItemDto } from './dto';
import { Request } from 'express';

@Controller('cart')
export class CartController {
  constructor(private readonly cartService: CartService) {}

  @Post('add')
  addItem(@Req() req: Request, @Body() createCartItemDto: CreateCartItemDto) {
    const userId = req.user.id;
    return this.cartService.addItem(userId, createCartItemDto);
  }

  @Patch('update/:itemId')
  updateItem(@Req() req: Request, @Param('itemId') itemId: number, @Body() updateCartItemDto: UpdateCartItemDto) {
    const userId = req.user.id;
    return this.cartService.updateItem(userId, itemId, updateCartItemDto);
  }

  @Delete('remove/:itemId')
  removeItem(@Req() req: Request, @Param('itemId') itemId: number) {
    const userId = req.user.id;
    return this.cartService.removeItem(userId, itemId);
  }

  @Get('summary')
  getCartSummary(@Req() req: Request) {
    const userId = req.user.id;
    return this.cartService.getCartSummary(userId);
  }

  @Post('checkout')
  async checkout(@Req() req: Request) {
    const userId = req.user.id;
    const cart = await this.cartService.getCartSummary(userId);
    // Integrate the order placement and payment here
    await this.cartService.clearCart(userId);
    return { message: 'Checkout successful' };
  }
}
```

### Payment Processing

#### Step 1: Payment Service Integration

Stripe integration has already been covered earlier. For PayPal, you can use the PayPal SDK. Below is an example for integrating PayPal.

Install PayPal SDK:

```bash
npm install @paypal/checkout-server-sdk
```

Create `paypal.service.ts`:

```typescript
import { Injectable } from '@nestjs/common';
import * as paypal from '@paypal/checkout-server-sdk';
import { OrdersService } from '../orders/orders.service';

@Injectable()
export class PaypalService {
  private environment: paypal.core.SandboxEnvironment;
  private client: paypal.core.PayPalHttpClient;

  constructor(private ordersService: OrdersService) {
    this.environment = new paypal.core.SandboxEnvironment('CLIENT_ID', 'CLIENT_SECRET');
    this.client = new paypal.core.PayPalHttpClient(this.environment);
  }

  async createOrder(orderId: number) {
    const order = await this.ordersService.findOrderById(orderId);

    const request = new paypal.orders.OrdersCreateRequest();
    request.prefer("return=representation");
    request.requestBody({
      intent: 'CAPTURE',
      purchase_units: [{
        amount: {
          currency_code: 'USD',
          value: order.total.toString(),
        },
      }],
    });

    const response = await this.client.execute(request);
    return response.result;
  }

  async captureOrder(orderId: string) {
    const request = new paypal.orders.OrdersCaptureRequest(orderId);
    request.requestBody({});

    const response = await this.client.execute(request);
    return response.result;
  }
}
```

#### Step 2: Payment Controller

Create `payments.controller.ts`:

```typescript
import { Controller, Post, Body, Req } from '@nestjs/common';
import { PaymentsService } from './payments.service';
import { PaypalService } from './paypal.service';
import { Request } from 'express';

@Controller('payments')
export class PaymentsController {
  constructor(
    private readonly paymentsService: PaymentsService,
    private readonly paypalService: PaypalService,
  ) {}

  @Post('stripe/create-payment-intent/:orderId')
  createStripePaymentIntent(@Param('orderId') orderId: number) {
    return this.paymentsService.create

PaymentIntent(orderId);
  }

  @Post('paypal/create-order/:orderId')
  createPaypalOrder(@Param('orderId') orderId: number) {
    return this.paypalService.createOrder(orderId);
  }

  @Post('paypal/capture-order/:orderId')
  capturePaypalOrder(@Param('orderId') orderId: string) {
    return this.paypalService.captureOrder(orderId);
  }
}
```

### Review and Rating

#### Step 1: Generate Review Module

Generate review module, service, and controller:

```bash
nest generate module reviews
nest generate service reviews
nest generate controller reviews
```

#### Step 2: Create Review Entity

Create `review.entity.ts` in the `reviews` folder:

```typescript
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { User } from '../users/user.entity';
import { Product } from '../products/product.entity';

@Entity()
export class Review {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  rating: number;

  @Column()
  comment: string;

  @ManyToOne(() => User, user => user.reviews)
  user: User;

  @ManyToOne(() => Product, product => product.reviews)
  product: Product;
}
```

#### Step 3: Create Review DTOs

Create `create-review.dto.ts` and `update-review.dto.ts` in the `reviews` folder:

**create-review.dto.ts**:
```typescript
import { IsNotEmpty, IsNumber, IsString, Min, Max } from 'class-validator';

export class CreateReviewDto {
  @IsNotEmpty()
  @IsNumber()
  @Min(1)
  @Max(5)
  rating: number;

  @IsNotEmpty()
  @IsString()
  comment: string;
}
```

**update-review.dto.ts**:
```typescript
import { IsNotEmpty, IsNumber, IsString, Min, Max } from 'class-validator';

export class UpdateReviewDto {
  @IsNotEmpty()
  @IsNumber()
  @Min(1)
  @Max(5)
  rating: number;

  @IsNotEmpty()
  @IsString()
  comment: string;
}
```

#### Step 4: Set Up Review Service

In `reviews.service.ts`:

```typescript
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Review } from './review.entity';
import { CreateReviewDto, UpdateReviewDto } from './dto';
import { UsersService } from '../users/users.service';
import { ProductsService } from '../products/products.service';

@Injectable()
export class ReviewsService {
  constructor(
    @InjectRepository(Review)
    private reviewsRepository: Repository<Review>,
    private usersService: UsersService,
    private productsService: ProductsService,
  ) {}

  async addReview(userId: number, productId: number, createReviewDto: CreateReviewDto): Promise<Review> {
    const user = await this.usersService.findOneById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    const product = await this.productsService.findProductById(productId);
    if (!product) {
      throw new NotFoundException('Product not found');
    }

    const review = this.reviewsRepository.create({ ...createReviewDto, user, product });
    return this.reviewsRepository.save(review);
  }

  async updateReview(userId: number, reviewId: number, updateReviewDto: UpdateReviewDto): Promise<Review> {
    const review = await this.reviewsRepository.findOne({ where: { id: reviewId, user: { id: userId } } });
    if (!review) {
      throw new NotFoundException('Review not found');
    }

    review.rating = updateReviewDto.rating;
    review.comment = updateReviewDto.comment;
    return this.reviewsRepository.save(review);
  }

  async deleteReview(userId: number, reviewId: number): Promise<void> {
    const review = await this.reviewsRepository.findOne({ where: { id: reviewId, user: { id: userId } } });
    if (!review) {
      throw new NotFoundException('Review not found');
    }

    await this.reviewsRepository.remove(review);
  }

  async getProductReviews(productId: number): Promise<Review[]> {
    return this.reviewsRepository.find({ where: { product: { id: productId } }, relations: ['user'] });
  }
}
```

#### Step 5: Set Up Review Controller

In `reviews.controller.ts`:

```typescript
import { Controller, Post, Get, Patch, Delete, Param, Body, Req } from '@nestjs/common';
import { ReviewsService } from './reviews.service';
import { CreateReviewDto, UpdateReviewDto } from './dto';
import { Request } from 'express';

@Controller('reviews')
export class ReviewsController {
  constructor(private readonly reviewsService: ReviewsService) {}

  @Post(':productId')
  addReview(@Req() req: Request, @Param('productId') productId: number, @Body() createReviewDto: CreateReviewDto) {
    const userId = req.user.id;
    return this.reviewsService.addReview(userId, productId, createReviewDto);
  }

  @Patch(':reviewId')
  updateReview(@Req() req: Request, @Param('reviewId') reviewId: number, @Body() updateReviewDto: UpdateReviewDto) {
    const userId = req.user.id;
    return this.reviewsService.updateReview(userId, reviewId, updateReviewDto);
  }

  @Delete(':reviewId')
  deleteReview(@Req() req: Request, @Param('reviewId') reviewId: number) {
    const userId = req.user.id;
    return this.reviewsService.deleteReview(userId, reviewId);
  }

  @Get('product/:productId')
  getProductReviews(@Param('productId') productId: number) {
    return this.reviewsService.getProductReviews(productId);
  }
}
```

#### Step 6: Register Modules

Update the main module to include the cart, payments, and reviews modules:

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { ProductsModule } from './products/products.module';
import { OrdersModule } from './orders/orders.module';
import { CartModule } from './cart/cart.module';
import { PaymentsModule } from './payments/payments.module';
import { ReviewsModule } from './reviews/reviews.module';
import { User } from './users/user.entity';
import { Product } from './products/product.entity';
import { Category } from './products/category.entity';
import { Order, OrderItem } from './orders/order.entity';
import { Cart, CartItem } from './cart/cart.entity';
import { Review } from './reviews/review.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'data.db',
      entities: [User, Product, Category, Order, OrderItem, Cart, CartItem, Review],
      synchronize: true,
    }),
    UsersModule,
    AuthModule,
    ProductsModule,
    OrdersModule,
    CartModule,
    PaymentsModule,
    ReviewsModule,
  ],
})
export class AppModule {}
```

### Summary

This code provides a complete backend implementation for an e-commerce platform including shopping cart management, payment processing with Stripe and PayPal, and review and rating features. Each section contains fully functional and proper code to cover the specified functionalities. You can further customize and extend this base to fit additional requirements and integrate with a frontend.

Certainly! Here’s how you can add functionalities for Wishlist, Search and Filtering, and Notifications to your e-commerce backend.

### Wishlist

#### Step 1: Generate Wishlist Module

Generate the wishlist module, service, and controller:

```bash
nest generate module wishlist
nest generate service wishlist
nest generate controller wishlist
```

#### Step 2: Create Wishlist Entity

Create `wishlist.entity.ts` in the `wishlist` folder:

```typescript
import { Entity, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { User } from '../users/user.entity';
import { Product } from '../products/product.entity';

@Entity()
export class Wishlist {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => User, user => user.wishlist)
  user: User;

  @ManyToOne(() => Product, product => product.id)
  product: Product;
}
```

#### Step 3: Set Up Wishlist Service

In `wishlist.service.ts`:

```typescript
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Wishlist } from './wishlist.entity';
import { UsersService } from '../users/users.service';
import { ProductsService } from '../products/products.service';

@Injectable()
export class WishlistService {
  constructor(
    @InjectRepository(Wishlist)
    private wishlistRepository: Repository<Wishlist>,
    private usersService: UsersService,
    private productsService: ProductsService,
  ) {}

  async addToWishlist(userId: number, productId: number): Promise<Wishlist> {
    const user = await this.usersService.findOneById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    const product = await this.productsService.findProductById(productId);
    if (!product) {
      throw new NotFoundException('Product not found');
    }

    const wishlistItem = this.wishlistRepository.create({ user, product });
    return this.wishlistRepository.save(wishlistItem);
  }

  async removeFromWishlist(userId: number, productId: number): Promise<void> {
    const wishlistItem = await this.wishlistRepository.findOne({ where: { user: { id: userId }, product: { id: productId } } });
    if (!wishlistItem) {
      throw new NotFoundException('Wishlist item not found');
    }

    await this.wishlistRepository.remove(wishlistItem);
  }

  async viewWishlist(userId: number): Promise<Wishlist[]> {
    return this.wishlistRepository.find({ where: { user: { id: userId } }, relations: ['product'] });
  }
}
```

#### Step 4: Set Up Wishlist Controller

In `wishlist.controller.ts`:

```typescript
import { Controller, Post, Delete, Get, Param, Req } from '@nestjs/common';
import { WishlistService } from './wishlist.service';
import { Request } from 'express';

@Controller('wishlist')
export class WishlistController {
  constructor(private readonly wishlistService: WishlistService) {}

  @Post(':productId')
  addToWishlist(@Req() req: Request, @Param('productId') productId: number) {
    const userId = req.user.id;
    return this.wishlistService.addToWishlist(userId, productId);
  }

  @Delete(':productId')
  removeFromWishlist(@Req() req: Request, @Param('productId') productId: number) {
    const userId = req.user.id;
    return this.wishlistService.removeFromWishlist(userId, productId);
  }

  @Get()
  viewWishlist(@Req() req: Request) {
    const userId = req.user.id;
    return this.wishlistService.viewWishlist(userId);
  }
}
```

### Search and Filtering

#### Step 1: Update Product Module for Search and Filtering

In `products.service.ts`:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Product } from './product.entity';

@Injectable()
export class ProductsService {
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
  ) {}

  async searchProducts(query: string): Promise<Product[]> {
    return this.productsRepository.createQueryBuilder('product')
      .where('product.name LIKE :query', { query: `%${query}%` })
      .orWhere('product.description LIKE :query', { query: `%${query}%` })
      .getMany();
  }

  async filterProducts(categoryId?: number, minPrice?: number, maxPrice?: number, minRating?: number): Promise<Product[]> {
    let queryBuilder = this.productsRepository.createQueryBuilder('product');

    if (categoryId) {
      queryBuilder = queryBuilder.andWhere('product.category.id = :categoryId', { categoryId });
    }

    if (minPrice) {
      queryBuilder = queryBuilder.andWhere('product.price >= :minPrice', { minPrice });
    }

    if (maxPrice) {
      queryBuilder = queryBuilder.andWhere('product.price <= :maxPrice', { maxPrice });
    }

    if (minRating) {
      queryBuilder = queryBuilder.andWhere('product.rating >= :minRating', { minRating });
    }

    return queryBuilder.getMany();
  }
}
```

In `products.controller.ts`:

```typescript
import { Controller, Get, Query } from '@nestjs/common';
import { ProductsService } from './products.service';

@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Get('search')
  searchProducts(@Query('query') query: string) {
    return this.productsService.searchProducts(query);
  }

  @Get('filter')
  filterProducts(
    @Query('categoryId') categoryId?: number,
    @Query('minPrice') minPrice?: number,
    @Query('maxPrice') maxPrice?: number,
    @Query('minRating') minRating?: number,
  ) {
    return this.productsService.filterProducts(categoryId, minPrice, maxPrice, minRating);
  }
}
```

### Notifications

#### Step 1: Generate Notifications Module

Generate the notifications module, service, and controller:

```bash
nest generate module notifications
nest generate service notifications
nest generate controller notifications
```

#### Step 2: Set Up Email Notifications

Install `nodemailer` for email notifications:

```bash
npm install nodemailer
```

In `notifications.service.ts`:

```typescript
import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';

@Injectable()
export class NotificationsService {
  private transporter: nodemailer.Transporter;

  constructor() {
    this.transporter = nodemailer.createTransport({
      service: 'gmail',
      auth: {
        user: 'your-email@gmail.com',
        pass: 'your-email-password',
      },
    });
  }

  async sendOrderConfirmation(email: string, orderId: number) {
    const mailOptions = {
      from: 'your-email@gmail.com',
      to: email,
      subject: 'Order Confirmation',
      text: `Your order with ID ${orderId} has been confirmed.`,
    };

    await this.transporter.sendMail(mailOptions);
  }

  async sendShippingUpdate(email: string, orderId: number, status: string) {
    const mailOptions = {
      from: 'your-email@gmail.com',
      to: email,
      subject: 'Shipping Update',
      text: `Your order with ID ${orderId} is now ${status}.`,
    };

    await this.transporter.sendMail(mailOptions);
  }
}
```

#### Step 3: Set Up SMS Notifications

Install `twilio` for SMS notifications:

```bash
npm install twilio
```

In `notifications.service.ts`:

```typescript
import * as Twilio from 'twilio';

@Injectable()
export class NotificationsService {
  private twilioClient: Twilio.Twilio;

  constructor() {
    this.twilioClient = Twilio('ACCOUNT_SID', 'AUTH_TOKEN');
  }

  async sendOrderConfirmationSMS(phone: string, orderId: number) {
    await this.twilioClient.messages.create({
      body: `Your order with ID ${orderId} has been confirmed.`,
      from: '+1234567890',
      to: phone,
    });
  }

  async sendShippingUpdateSMS(phone: string, orderId: number, status: string) {
    await this.twilioClient.messages.create({
      body: `Your order with ID ${orderId} is now ${status}.`,
      from: '+1234567890',
      to: phone,
    });
  }
}
```

#### Step 4: Set Up In-App Notifications

In `notifications.entity.ts`:

```typescript
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { User } from '../users/user.entity';

@Entity()
export class Notification {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  message: string;

  @ManyToOne(() => User, user => user.notifications)
  user: User;

  @Column({ default: false })
  read: boolean;
}
```

In `notifications.service.ts`:

```typescript
@Injectable()
export class NotificationsService {
  // Add necessary imports and constructor

  async createInAppNotification(userId: number, message: string) {
    const user = await this.usersService.findOneById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    const notification = this.notificationsRepository.create({ user, message });
    return this.notificationsRepository.save(notification);
  }

  async getNotifications(userId: number): Promise<Notification[]> {


    return this.notificationsRepository.find({ where: { user: { id: userId } } });
  }

  async markAsRead(notificationId: number) {
    const notification = await this.notificationsRepository.findOne(notificationId);
    if (!notification) {
      throw new NotFoundException('Notification not found');
    }

    notification.read = true;
    return this.notificationsRepository.save(notification);
  }
}
```

In `notifications.controller.ts`:

```typescript
import { Controller, Post, Get, Patch, Param, Req, Body } from '@nestjs/common';
import { NotificationsService } from './notifications.service';
import { Request } from 'express';

@Controller('notifications')
export class NotificationsController {
  constructor(private readonly notificationsService: NotificationsService) {}

  @Post()
  createNotification(@Req() req: Request, @Body('message') message: string) {
    const userId = req.user.id;
    return this.notificationsService.createInAppNotification(userId, message);
  }

  @Get()
  getNotifications(@Req() req: Request) {
    const userId = req.user.id;
    return this.notificationsService.getNotifications(userId);
  }

  @Patch(':notificationId')
  markAsRead(@Param('notificationId') notificationId: number) {
    return this.notificationsService.markAsRead(notificationId);
  }
}
```

#### Step 5: Register Modules

Update the main module to include the wishlist, notifications, and other modules:

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { ProductsModule } from './products/products.module';
import { OrdersModule } from './orders/orders.module';
import { CartModule } from './cart/cart.module';
import { PaymentsModule } from './payments/payments.module';
import { ReviewsModule } from './reviews/reviews.module';
import { WishlistModule } from './wishlist/wishlist.module';
import { NotificationsModule } from './notifications/notifications.module';
import { User } from './users/user.entity';
import { Product } from './products/product.entity';
import { Category } from './products/category.entity';
import { Order, OrderItem } from './orders/order.entity';
import { Cart, CartItem } from './cart/cart.entity';
import { Review } from './reviews/review.entity';
import { Wishlist } from './wishlist/wishlist.entity';
import { Notification } from './notifications/notification.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'data.db',
      entities: [User, Product, Category, Order, OrderItem, Cart, CartItem, Review, Wishlist, Notification],
      synchronize: true,
    }),
    UsersModule,
    AuthModule,
    ProductsModule,
    OrdersModule,
    CartModule,
    PaymentsModule,
    ReviewsModule,
    WishlistModule,
    NotificationsModule,
  ],
})
export class AppModule {}
```

### Summary

This code provides a complete backend implementation for an e-commerce platform including wishlist management, product search and filtering, and notifications (email, SMS, and in-app). Each section contains fully functional and proper code to cover the specified functionalities. You can further customize and extend this base to fit additional requirements and integrate with a frontend.

To provide a comprehensive solution, we'll set up a backend with NestJS and a frontend with Next.js and Tailwind CSS. 

### Backend (NestJS)

We already have most of the backend functionality from previous implementations. Now, let's extend it to include some admin-specific endpoints and enhance the existing modules to support admin operations.

#### User Management for Admin

We'll extend the existing Users module to allow the admin to manage users.

##### Users Controller (users.controller.ts)

Add endpoints for admin operations:

```typescript
import { Controller, Get, Param, Delete, UseGuards } from '@nestjs/common';
import { UsersService } from './users.service';
import { Roles } from '../auth/roles.decorator';
import { Role } from '../auth/role.enum';
import { RolesGuard } from '../auth/roles.guard';

@Controller('users')
@UseGuards(RolesGuard)
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  @Roles(Role.Admin)
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  @Roles(Role.Admin)
  findOne(@Param('id') id: number) {
    return this.usersService.findOneById(id);
  }

  @Delete(':id')
  @Roles(Role.Admin)
  remove(@Param('id') id: number) {
    return this.usersService.remove(id);
  }
}
```

##### Users Service (users.service.ts)

Extend the service to support these operations:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  findOneById(id: number): Promise<User> {
    return this.usersRepository.findOne(id);
  }

  async remove(id: number): Promise<void> {
    await this.usersRepository.delete(id);
  }
}
```

#### Product Management for Admin

Extend the existing Products module to allow admin operations:

##### Products Controller (products.controller.ts)

Add endpoints for admin operations:

```typescript
import { Controller, Get, Post, Body, Param, Patch, Delete, UseGuards } from '@nestjs/common';
import { ProductsService } from './products.service';
import { CreateProductDto, UpdateProductDto } from './dto';
import { Roles } from '../auth/roles.decorator';
import { Role } from '../auth/role.enum';
import { RolesGuard } from '../auth/roles.guard';

@Controller('products')
@UseGuards(RolesGuard)
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Post()
  @Roles(Role.Admin)
  create(@Body() createProductDto: CreateProductDto) {
    return this.productsService.create(createProductDto);
  }

  @Get()
  findAll() {
    return this.productsService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: number) {
    return this.productsService.findProductById(id);
  }

  @Patch(':id')
  @Roles(Role.Admin)
  update(@Param('id') id: number, @Body() updateProductDto: UpdateProductDto) {
    return this.productsService.update(id, updateProductDto);
  }

  @Delete(':id')
  @Roles(Role.Admin)
  remove(@Param('id') id: number) {
    return this.productsService.remove(id);
  }
}
```

##### Products Service (products.service.ts)

Extend the service to support these operations:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Product } from './product.entity';
import { CreateProductDto, UpdateProductDto } from './dto';

@Injectable()
export class ProductsService {
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
  ) {}

  create(createProductDto: CreateProductDto): Promise<Product> {
    const product = this.productsRepository.create(createProductDto);
    return this.productsRepository.save(product);
  }

  findAll(): Promise<Product[]> {
    return this.productsRepository.find();
  }

  findProductById(id: number): Promise<Product> {
    return this.productsRepository.findOne(id);
  }

  async update(id: number, updateProductDto: UpdateProductDto): Promise<Product> {
    await this.productsRepository.update(id, updateProductDto);
    return this.productsRepository.findOne(id);
  }

  async remove(id: number): Promise<void> {
    await this.productsRepository.delete(id);
  }
}
```

#### Order Management for Admin

Extend the existing Orders module to allow admin operations:

##### Orders Controller (orders.controller.ts)

Add endpoints for admin operations:

```typescript
import { Controller, Get, Param, Patch, Delete, UseGuards } from '@nestjs/common';
import { OrdersService } from './orders.service';
import { Roles } from '../auth/roles.decorator';
import { Role } from '../auth/role.enum';
import { RolesGuard } from '../auth/roles.guard';

@Controller('orders')
@UseGuards(RolesGuard)
export class OrdersController {
  constructor(private readonly ordersService: OrdersService) {}

  @Get()
  @Roles(Role.Admin)
  findAll() {
    return this.ordersService.findAll();
  }

  @Get(':id')
  @Roles(Role.Admin)
  findOne(@Param('id') id: number) {
    return this.ordersService.findOne(id);
  }

  @Patch(':id')
  @Roles(Role.Admin)
  updateStatus(@Param('id') id: number, @Body('status') status: string) {
    return this.ordersService.updateStatus(id, status);
  }

  @Delete(':id')
  @Roles(Role.Admin)
  remove(@Param('id') id: number) {
    return this.ordersService.remove(id);
  }
}
```

##### Orders Service (orders.service.ts)

Extend the service to support these operations:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order } from './order.entity';

@Injectable()
export class OrdersService {
  constructor(
    @InjectRepository(Order)
    private ordersRepository: Repository<Order>,
  ) {}

  findAll(): Promise<Order[]> {
    return this.ordersRepository.find();
  }

  findOne(id: number): Promise<Order> {
    return this.ordersRepository.findOne(id);
  }

  async updateStatus(id: number, status: string): Promise<Order> {
    await this.ordersRepository.update(id, { status });
    return this.ordersRepository.findOne(id);
  }

  async remove(id: number): Promise<void> {
    await this.ordersRepository.delete(id);
  }
}
```

#### Sales Reports and Analytics

For sales reports and analytics, we will create a new service that fetches data from the existing entities and provides aggregated information.

##### Reports Controller (reports.controller.ts)

Generate a reports controller:

```bash
nest generate controller reports
```

Add endpoints for sales reports and analytics:

```typescript
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
import { ReportsService } from './reports.service';
import { Roles } from '../auth/roles.decorator';
import { Role } from '../auth/role.enum';
import { RolesGuard } from '../auth/roles.guard';

@Controller('reports')
@UseGuards(RolesGuard)
export class ReportsController {
  constructor(private readonly reportsService: ReportsService) {}

  @Get('sales')
  @Roles(Role.Admin)
  getSalesReport(@Query('startDate') startDate: string, @Query('endDate') endDate: string) {
    return this.reportsService.getSalesReport(new Date(startDate), new Date(endDate));
  }

  @Get('analytics')
  @Roles(Role.Admin)
  getAnalytics() {
    return this.reportsService.getAnalytics();
  }
}
```

##### Reports Service (reports.service.ts)

Generate a reports service:

```bash
nest generate service reports
```

Implement the service:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order } from '../orders/order.entity';

@Injectable()
export class ReportsService {
  constructor(
    @InjectRepository(Order)
    private ordersRepository: Repository<Order>,
  ) {}

  async getSalesReport(startDate: Date, endDate: Date) {
    const orders = await this.ordersRepository.createQueryBuilder('order')
      .where('order.createdAt BETWEEN :startDate AND :endDate', { startDate, endDate })
      .getMany();

    const totalSales = orders.reduce((sum, order) => sum + order.totalPrice, 0);
    const totalOrders = orders.length;

    return { totalSales, totalOrders, orders };
  }

  async getAnalytics() {
    const totalUsers = await this.ordersRepository.query('SELECT COUNT(*) FROM user');
    const totalProducts = await this.ordersRepository.query('SELECT COUNT(*) FROM product');
    const totalOrders = await this.ordersRepository.query('SELECT COUNT(*) FROM "order"');

    return { totalUsers: totalUsers[0].count, totalProducts: totalProducts[0].count, totalOrders: totalOrders[0].count };
  }
}
```

### Frontend (Next.js and Tailwind CSS)

Let's set up a frontend with Next.js and Tailwind CSS for the admin dashboard.

#### Step 1: Set Up Next.js

Create a new Next.js project:

```bash
npx create-next

-app admin-dashboard
cd admin-dashboard
```

#### Step 2: Install Tailwind CSS

Follow the Tailwind CSS installation steps:

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

Add Tailwind CSS to your CSS files:

`tailwind.config.js`

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

`styles/globals.css`

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

#### Step 3: Create Pages and Components

Create the necessary pages and components for the admin dashboard.

##### Dashboard Layout

Create a layout for the dashboard:

`components/Layout.js`

```jsx
import Link from 'next/link';

const Layout = ({ children }) => {
  return (
    <div className="flex">
      <nav className="w-64 bg-gray-800 text-white h-screen p-5">
        <ul>
          <li className="mb-4">
            <Link href="/admin/users">User Management</Link>
          </li>
          <li className="mb-4">
            <Link href="/admin/products">Product Management</Link>
          </li>
          <li className="mb-4">
            <Link href="/admin/orders">Order Management</Link>
          </li>
          <li className="mb-4">
            <Link href="/admin/reports">Sales Reports</Link>
          </li>
          <li className="mb-4">
            <Link href="/admin/analytics">Analytics</Link>
          </li>
        </ul>
      </nav>
      <main className="flex-1 p-5">
        {children}
      </main>
    </div>
  );
};

export default Layout;
```

##### Pages

Create the main admin dashboard page:

`pages/admin/index.js`

```jsx
import Layout from '../../components/Layout';

const AdminDashboard = () => {
  return (
    <Layout>
      <h1 className="text-2xl font-bold">Admin Dashboard</h1>
    </Layout>
  );
};

export default AdminDashboard;
```

Create pages for each section (e.g., User Management, Product Management, Order Management, Sales Reports, Analytics).

`pages/admin/users.js`

```jsx
import Layout from '../../components/Layout';

const UserManagement = () => {
  // Fetch and display users here
  return (
    <Layout>
      <h1 className="text-2xl font-bold">User Management</h1>
      {/* User management code here */}
    </Layout>
  );
};

export default UserManagement;
```

Similarly, create `products.js`, `orders.js`, `reports.js`, and `analytics.js` under the `pages/admin` directory.

#### Step 4: Fetch Data from Backend

Use `axios` to fetch data from the backend:

```bash
npm install axios
```

Example in `users.js`:

```jsx
import { useEffect, useState } from 'react';
import axios from 'axios';
import Layout from '../../components/Layout';

const UserManagement = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    axios.get('/api/users')
      .then(response => setUsers(response.data))
      .catch(error => console.error(error));
  }, []);

  return (
    <Layout>
      <h1 className="text-2xl font-bold">User Management</h1>
      <table className="min-w-full table-auto">
        <thead>
          <tr>
            <th className="px-4 py-2">ID</th>
            <th className="px-4 py-2">Name</th>
            <th className="px-4 py-2">Email</th>
            <th className="px-4 py-2">Actions</th>
          </tr>
        </thead>
        <tbody>
          {users.map(user => (
            <tr key={user.id}>
              <td className="border px-4 py-2">{user.id}</td>
              <td className="border px-4 py-2">{user.name}</td>
              <td className="border px-4 py-2">{user.email}</td>
              <td className="border px-4 py-2">
                {/* Add action buttons here */}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </Layout>
  );
};

export default UserManagement;
```

Repeat similar steps for other pages (`products.js`, `orders.js`, `reports.js`, `analytics.js`).

### Summary

This solution sets up a comprehensive backend with NestJS for managing users, products, orders, sales reports, and analytics, as well as a frontend with Next.js and Tailwind CSS for the admin dashboard. The frontend includes a layout and pages for each section, fetching data from the backend to display and manage the information. You can further customize and extend this base to fit additional requirements.

Disclaimer: This content is generated by AI.