DEV Community

Cover image for Building an E-Commerce Platform with Full CRUD Operations Using TypeORM Relations in NestJS
BuildWithGagan
BuildWithGagan

Posted on

Building an E-Commerce Platform with Full CRUD Operations Using TypeORM Relations in NestJS

Introduction

Developing an e-commerce platform requires seamless integration of various entities like users, products, orders, categories, and more. Using NestJS with TypeORM simplifies this process by providing a structured way to handle relationships and CRUD operations. In this guide, we will explore creating an e-commerce backend from scratch, covering all TypeORM relationships with PostgreSQL as our database.


Why Use TypeORM With NestJS?

  • Object Relational Mapping: Simplifies database operations.
  • Relations: Manage one-to-one, one-to-many, and many-to-many relationships efficiently.
  • Declarative Approach: Use decorators to define entity behavior.

Prerequisites

Before starting, ensure you have the following installed:

  • Node.js: For running the backend.
  • NestJS CLI: For generating NestJS resources.
  • PostgreSQL: As the relational database.
npm install -g @nestjs/cli
Enter fullscreen mode Exit fullscreen mode

Setting Up NestJS and TypeORM

  1. Create a New Project:
   nest new ecommerce-platform
Enter fullscreen mode Exit fullscreen mode
  1. Install Dependencies:
   npm install @nestjs/typeorm typeorm pg
Enter fullscreen mode Exit fullscreen mode
  1. Configure TypeORM: Update src/app.module.ts:
   import { Module } from '@nestjs/common';
   import { TypeOrmModule } from '@nestjs/typeorm';

   @Module({
     imports: [
       TypeOrmModule.forRoot({
         type: 'postgres',
         host: 'localhost',
         port: 5432,
         username: 'your-username',
         password: 'your-password',
         database: 'ecommerce',
         entities: [__dirname + '/../**/*.entity{.ts,.js}'],
         synchronize: true, // Avoid in production
       }),
     ],
   })
   export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Designing the Database Schema

For an e-commerce platform, the following entities are essential:

  • User: Represents a customer or admin.
  • Product: Represents items for sale.
  • Category: Groups products.
  • Order: Tracks purchases.

Creating Entities With TypeORM Relations

  1. User Entity:
   import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
   import { Order } from './order.entity';

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

     @Column()
     name: string;

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

     @OneToMany(() => Order, (order) => order.user)
     orders: Order[];
   }
Enter fullscreen mode Exit fullscreen mode
  1. Product Entity:
   import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
   import { Category } from './category.entity';

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

     @Column()
     name: string;

     @Column('decimal')
     price: number;

     @ManyToOne(() => Category, (category) => category.products)
     category: Category;
   }
Enter fullscreen mode Exit fullscreen mode
  1. Category Entity:
   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[];
   }
Enter fullscreen mode Exit fullscreen mode
  1. Order Entity:
   import { Entity, PrimaryGeneratedColumn, ManyToOne, ManyToMany, JoinTable } from 'typeorm';
   import { User } from './user.entity';
   import { Product } from './product.entity';

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

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

     @ManyToMany(() => Product)
     @JoinTable()
     products: Product[];
   }
Enter fullscreen mode Exit fullscreen mode

Implementing CRUD Operations

  1. Generate Resources:
   nest g resource users
   nest g resource products
   nest g resource categories
   nest g resource orders
Enter fullscreen mode Exit fullscreen mode
  1. User Service:
   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 userRepository: Repository<User>,
     ) {}

     createUser(user: Partial<User>) {
       return this.userRepository.save(user);
     }

     findAllUsers() {
       return this.userRepository.find({ relations: ['orders'] });
     }

     findUserById(id: number) {
       return this.userRepository.findOne({ where: { id }, relations: ['orders'] });
     }

     updateUser(id: number, updateUser: Partial<User>) {
       return this.userRepository.update(id, updateUser);
     }

     deleteUser(id: number) {
       return this.userRepository.delete(id);
     }
   }
Enter fullscreen mode Exit fullscreen mode

Handling Relationships in CRUD Operations

Example: Create an Order With Products:

async createOrder(userId: number, productIds: number[]) {
  const user = await this.userRepository.findOne({ where: { id: userId } });
  const products = await this.productRepository.findByIds(productIds);

  const order = new Order();
  order.user = user;
  order.products = products;

  return this.orderRepository.save(order);
}
Enter fullscreen mode Exit fullscreen mode

Example: Fetch Orders With User and Products:

async findOrders() {
  return this.orderRepository.find({ relations: ['user', 'products'] });
}
Enter fullscreen mode Exit fullscreen mode

Validating and Securing Your API

Use DTOs (Data Transfer Objects) with class-validator for validation:

npm install class-validator class-transformer
Enter fullscreen mode Exit fullscreen mode

Example DTO:

import { IsNotEmpty, IsEmail } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty()
  name: string;

  @IsEmail()
  email: string;
}
Enter fullscreen mode Exit fullscreen mode

Controller:

@Post()
createUser(@Body() createUserDto: CreateUserDto) {
  return this.userService.createUser(createUserDto);
}
Enter fullscreen mode Exit fullscreen mode

Frequently Asked Questions

1. Why Use TypeORM for E-Commerce?

TypeORM simplifies working with relational databases, making it ideal for handling complex relationships in e-commerce platforms.

2. How Do I Avoid Circular Dependencies in Relations?

Use lazy relations or modularize your entities by grouping related ones into specific modules.

3. How Can I Improve Performance?

  • Use pagination for large datasets.
  • Use indexes on frequently queried columns.

Conclusion

By following this step-by-step guide, you have created an e-commerce backend using NestJS and TypeORM with PostgreSQL. This implementation supports CRUD operations for users, products, categories, and orders with all necessary relationships. Experiment further by adding features like authentication, inventory management, and reporting.

Top comments (0)