the-fayed / e-commerce

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

RESTful E-Commerce API

Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge

Tabel of content

Key feature

  1. Authentication:

The user can sign up, and after that a verification email will be sent to his inbox to verify his email, then he can log in to the system.

  • A new Admin can be added only by another admin.
  1. Authorization:
  • Admin:

    • Can add, modify, and delete categories, subcategories, brands, products, and coupons.
    • Can add, modify, and delete either users or admins.
    • Can add, modify, and delete app settings like tax price and shipment price.
    • Can delete offensive user's reviews.
    • Can modify order status like if the order has been delivered, and if the order has been paid if it was a cash order.
  • User:

    • Can modify his own data like addresses, email or phone number.
    • Can modify his password or reset it.
    • Can add products to his wishlist.
    • Can add items to his cart.
    • Can do either cash or online orders.
    • Can add only one review for each product.
  1. User wishlist.
  2. User cart.
  3. User addresses.
  4. Online orders.
  5. Cash orders.
  6. Product review and rating.

Installation

First, clone a fresh copy:

git clone https://github.com/the-fayed/e-commerce.git

Then, you need to run npm install to install app dependencies.

Finally, you need to set up the environment variables:

# DB
DB_CONNECTION_STRING: either a local or atlas mongodb connection

# APP SETTINGS
NODE_ENV: either development or production
PORT: listen port for the app
BASE_URL: app baseurl

# JWT
SECRET_KEY: create a JWT secret key of at least 32 characters.
EXPIRATION_PERIOD: how long the JWT user access token will be valid

# NODEMAILER
EMAIL_HOST: ex. smtp.gmail.com
EMAIL_PORT: ex. 587 and the secure option will be set as false
EMAIL_USER: sender email
EMAIL_PASSWORD: sender password

# STRIPE SETTINGS
STRIPE_API_KEY: strip the secret api key, and the public key will be sent to the front-end developer
STRIPE_WEBHOOK_KEY: webhook secret key

# CSRF
CSRF_SECRET: create a CSRF secret key of at least 32 characters.

Schemas

Category Schema

  {
    name: {
      type: String,
      required: true,
      unique: true,
      minlength: [3, `Too short category name`],
      maxlength: [32, `Too long category name`],
    },
    slug: {
      type: String,
      required: true,
      unique: true,
      lowercase: true,
    },
    image: {
      type: String,
    },
  }

Subcategory schema

  {
    name: {
      type: String,
      required: true,
      minlength: [2, `Too short subcategory name`],
      maxlength: [32, `Too long subcategory name`],
      unique: true,
    },
    slug: {
      type: String,
      required: true,
      unique: true,
      lowercase: true,
    },
    category: {
      type: mongoose.Schema.Types.ObjectId,
      ref: `category`,
      required: true,
    },
  }

Brand schema

{
  name: {
    type: String,
    required: true,
    unique: true,
    minlength: [2, `Too short brand name`],
    maxlength: [32, `Too long brand name`],
  },
  slug: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  image: {
    type: String,
  }
}

Product schema

  {
    title: {
      type: String,
      required: true,
      minlength: [2, `Too short product name`],
      maxlength: [100, `Too long product name`],
      unique: [true, `Product name must be unique`],
    },
    slug: {
      type: String,
      required: true,
      minlength: [2, `Too short product slug`],
      maxlength: [100, `Too long product slug`],
      unique: true,
      lowercase: true,
    },
    description: {
      type: String,
      required: true,
      minlength: [2, `Too short product description`],
      maxlength: [2000, `Too long product description`],
    },
    price: {
      type: Number,
      required: true,
      min: [0, `Price cannot be negative`],
      maxlength: [2000, `Too long product price`],
    },
    category: {
      type: mongoose.Schema.Types.ObjectId,
      ref: `category`,
      required: true,
    },
    subcategories: [
      {
        type: mongoose.Schema.Types.ObjectId,
        ref: `subcategory`,
      },
    ],
    brand: {
      type: mongoose.Schema.Types.ObjectId,
      ref: `brand`,
    },
    images: [String],
    cover: {
      type: String,
      required: true,
    },
    quantity: {
      type: Number,
      required: true,
    },
    sold: {
      type: Number,
      default: 0,
    },
    priceAfterDiscount: {
      type: Number,
    },
    colors: [
      {
        type: String,
      },
    ],
    sizes: [
      {
        type: String
      }
    ],
    ratingsAverage: {
      type: Number,
      min: 1,
      max: 5,
    },
    ratingsQuantity: {
      type: Number,
      default: 0,
    },
  }

User schema

  {
    name: {
      type: String,
      required: [true, `name is required`],
      trim: true,
    },
    slug: {
      type: String,
      lowercase: true,
    },
    email: {
      type: String,
      required: [true, `email is required`],
      lowercase: true,
      unique: true,
      trim: true,
    },
    phone: {
      type: String,
    },
    avatar: {
      type: String,
    },
    password: {
      type: String,
      required: [true, `password is required`],
      minlength: [8, `Too short password`],
    },
    wishlist: [
      {
        type: mongoose.Schema.Types.ObjectId,
        ref: "product",
      },
    ],
    addresses: [
      {
        id: mongoose.Schema.Types.ObjectId,
        alias: String,
        details: String,
        phone: String,
        city: String,
        postalCode: String,
      },
    ],
    passwordChangedAt: Date,
    resetPasswordCode: String,
    resetPasswordExpire: Date,
    resetCodeVerified: Boolean,
    role: {
      type: String,
      eunm: [`admin`, `user`],
      default: `user`,
    },
    emailVerified: {
      type: Boolean,
      default: false,
    },
    verifyEmailToken: String,
  }

Cart schema

  {
    items: [
      {
        product: {
          type: mongoose.Schema.Types.ObjectId,
          ref: "product",
          required: [true, "product id is required"],
        },
        quantity: {
          type: Number,
          default: 1,
        },
        color: String,
        size: String,
        price: Number,
      },
    ],
    totalPrice: Number,
    totalPriceAfterDiscount: Number,
    user: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "user",
      required: [true, "User id is required"],
    },
  }

Review schema

  {
    user: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "user",
      required: true,
    },
    product: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "product",
      required: true,
    },
    tile: {
      type: String,
    },
    rating: {
      type: Number,
      min: [1, "Minimum ratings value is 1.0"],
      max: [5, "Max ratings value is 5.0"],
      required: true,
    },
  }

note that after any new review total reviews and average rating for the product reviewed will be change automatically, and any user can review every product one time only.

Coupon schema

  {
    name: {
      type: String,
      required: [true, "Coupon name is required"],
      trim: true,
    },
    expire: {
      type: Date,
      required: [true, "Coupon expire date is required"],
    },
    discount: {
      type: Number,
      required: [true, "Coupon discount is required"],
    },
  }

Order schema

  {
    user: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "user",
      required: [true, "user id is required"],
    },
    items: [
      {
        product: {
          type: mongoose.Schema.Types.ObjectId,
          ref: "product",
          required: [true, "product id is required"],
        },
        quantity: {
          type: Number,
          default: 1,
        },
        color: String,
        size: String,
        price: Number,
      },
    ],
    taxPrice: {
      type: Number,
      default: 0,
    },
    shipmentPrice: {
      type: Number,
      default: 0,
    },
    shippingAddress: {
      details: String,
      phone: String,
      city: String,
      postalCode: String
    },
    totalPrice: Number,
    paymentMethod: {
      type: String,
      enum: ["online", "cash"],
      default: "cash",
    },
    isPaid: {
      type: Boolean,
      default: false,
    },
    paidAt: Date,
    isDelivered: {
      type: Boolean,
      default: false,
    },
    deliveredAt: Date,
  }

note that after every new order total stock of every product will be decreased automatically by the quantity of it on that order, on the other hand, the total sold of this product will be increased automatically by the quantity of it on that order.

App settings schema

  {
    taxPrice: {
      type: Number,
      required: true,
    },
    shipmentPrice: {
      type: Number,
      required: true,
    },
    createdBy: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "user",
      required: true,
    },
  }

Endpoints

Auth endpoints

Sign up endpoint

POST /api/v1/auth/signup
  • Open endpoint.
  • Request body type: form-data.

Request body example:

Key Value
name new user
avatar user image
email user@email.com
password new password
passwordConfirmation new pawword

Verify email endpoint

GET /api/v1/auth/verify/:token

note that an email verification will be sent to the user's email address.

Log in endpoint

POST /api/v1/auth/login
  • Open endpoint.

Request body example:

{
    "email": "user@email.com",
    "password": "new password"
}

Fortgot password endpoint

POST /api/v1/auth/forgotPassword
  • Open endpoint

Request body example:

{
    "email": "user@email.com"
}

Verify password reset code endpoint

POST /api/v1/auth/verifyResetCode
  • Open endpoint

Request body example:

{
    "resetCode": 123456
}

Reset password endpoint

POST /api/v1/auth/resetPassword
  • Open endpoint

Request body example:

{
    "email": "user@email.com",
    "password": "new password"
    "passwordConfirmation": "new password"
}

User endpoints

Create new user endpoint

POST /api/v1/users
  • Allowed to: only admins.
  • Request body type: form-data

Request body example:

Key Value
name new admin
email admin@email.com
phone 010123456789
avatar user image
role admin
password new password
passwordConfirmation new password
emailVerified true

Get all users

GET /api/v1/users
  • Allowed to: only admins
  • Endpoint supports pagination by adding page and size to request query, e.g. /users?page=1&size=20.
  • Endpoint support field limiting by adding which fields to return in request query e.g. /users?fields=name,image.
  • Endpoint supports search by adding search keywords to request query, e.g. /users?keyword=user.
  • Endpoint supports sorting by adding a sort method to request query, e.g. /users?sort=name.

Get specific user

GET /api/v1/users/:id
  • Allowed to: only admins

Update specific user

PUT /api/v1/users/:id
  • Allowed to: only admins
  • Request body type: form-data

Request body example:

Key Value
name updated admin
email admin@email.com
phone 010123456789
avatar user image
role admin
emailVerified true

Update specific user password

PUT /api/v1/users/changePassword/:id
  • Allowed to: only admins

Request body example:

{
    "currentPassword": "password",
    "newPassword": "updated password",
    "passwordConfirmation": "updated password"
}

Delete specific user

DELETE /api/v1/users/:id
  • Allowed to: only admins

Get logged user data

GET /api/v1/users/getLoggedUser
  • Allowed to: users and admins

Updated logged user data

PUT /api/v1/user/updateLoggedUserData
  • Allowed to: users and admins
  • Request body type: form-data

Request body example:

Key Value
name admin
email user@email.com
phone 010123456789
avatar user image

Update logged user password

PUT /api/v1/user/updateLoggedUserPassword
  • Allowed to: users and admins

Request body example:

{
    "currentPassword": "password",
    "newPassword": "updated password",
    "passwordConfirmation": "updated password"
}

Delete logged user

DELETE /api/v1/user/deleteLoggedUser
  • Allowed to: users and admins

categories endpoints

Create a new category:

POST /api/v1/category
  • Allowed to: only admins.
  • Type: form data.

Request body example:

Key Value
name new category
image Category image

Get all categories:

GET /api/v1/categories
  • Open endpoint.
  • No authentication is required for this endpoint call.
  • Endpoint supports pagination by adding page and size to request query /categories?page=1&size=20.
  • Endpoint support field limiting by adding which fields to return in request query ex. /categories?fields=name,image.
  • Endpoint supports search by adding search keywords to request query, e.g. /categories?keyword=category.
  • Endpoint supports sorting by adding a sort method to request query, e.g. /categories?sort=name.

Get a specific category:

GET /api/v1/categories/:id
  • Open endpoint.
  • No authentication is required for this endpoint call.

Update specific category:

PUT /api/v1/categories/:id
  • Allowed to: only admins.
  • Type: form data.

Request body example:

Key Value
name Category name
image Category image

Delete specific category:

DELETE /api/v1/categories/:id
  • Allowed to: only admins.

Subcategories endpoints

Nested routes from category routes

Create a new subcategory:

POST /api/v1/categories/:categoryId/subcategories
  • Allowed to: only admins

Request body example:

{
  "name": "new subcategory"
}

Get all subcategories:

GET /api/v1/category/:categoryId/subcategories
  • Open endpoint.
  • No authentication is required for this endpoint call.
  • Endpoint supports pagination by adding page and size to request query /subcategory?page=1&size=20.
  • Endpoint supports field limiting by adding which fields to return in a request query, e.g. /subcategory?fields=name,image.
  • Endpoint supports search by adding search keywords to request query, e.g. /subcategory?keyword=subcategory.
  • Endpoint supports sorting by adding a sort method to request query, e.g. /subcategory?sort=name.

note that to get all subcategories not the subcategories on a specific category the endpoint will be GET /api/v1/subcategories.

Get a specific subcategory

GET /api/v1/subcategories/:id
  • Open endpoint.
  • No authentication is required for this endpoint call.

Update a specific subcategory:

PUT /api/v1/subcategories/:id
  • Allowed to: only admins

Request body example:

{
  "name": "updated subcategory"
}

Delete a specific subcategory:

DELETE /api/v1/subcategories/:id
  • Allowed to: only admins

Brands endpoints

Create a new brand

POST /api/v1/brands
  • Allowed to: only admins.
  • Type: form data.

Request body example:

Key Value
name Brand name
image Brand image

Get all brands:

GET /api/v1/brands
  • Open endpoint.
  • No authentication is required for this endpoint call.
  • Endpoint supports pagination by adding page and size to request query /brands?page=1&size=20.
  • Endpoint supports field limiting by adding which fields to return in a request query, e.g. /brands?fields=name,image.
  • Endpoint supports search by adding search keywords to request query, e.g. /brands?keyword=brands.
  • Endpoint supports sorting by adding a sort method to request query, e.g. /brands?sort=name.

Get a specific brand:

GET /api/v1/brand/:id
  • Open endpoint.
  • No authentication is required for this API call.

Update a specific brand:

PUT /api/v1/brands/:id
  • Allowed to: only admins
  • Request body type: form-data

Request body example:

Key Value
name Brand name
image Brand image

Delete a specific brand:

DELETE /api/v1/brands/:id
  • Allowed to: only admins

Products endpoints

Create a new product

POST /api/v1/products
  • Allowed to: only admin.
  • Request body type: form-data

Request body example:

Key Value
title new product
description new product description
price 3000
priceAfterDiscount 2500
quantity 15
category 650b02996dde3fe0155a2f5a
subcategories 650b02996dde3fe0155a2f5a, 650b02996dde3fe0155a2f5b
brand 650b02996dde3fe0155a2f5a
cover product cover image
images product image
images product image

Get all products:

GET /api/v1/products
  • Open endpoint.
  • No authentication is required for this endpoint call.
  • Endpoint supports pagination by adding page and size to request query /products?page=1&size=20.
  • Endpoint supports field limiting by adding which fields to return in a request query, e.g. /products?fields=name,image.
  • Endpoint supports search by adding search keywords to request query, e.g. /products?keyword=products.
  • Endpoint supports sorting by adding a sort method to request query, e.g. /products?sort=name.

Get a specific product:

GET /api/v1/products/:id
  • Open endpoint.
  • No authentication is required for this endpoint call.

Update a specific product

PUT /api/v1/products/:id
  • Allowed to: only admins.
  • Request body type: form-data.

Request body example:

Key Value
title updated product
description updated product description
price 3000
quantity 20
category 650b02996dde3fe0155a2f5a
subcategories 650b02996dde3fe0155a2f5a, 650b02996dde3fe0155a2f5b
brand 650b02996dde3fe0155a2f5a
cover product cover image
images product image
images product image

Delete a specific product

DELETE /api/v1/products/:id
  • Allowed to: only admins.

Wishlist endpoints

Add product to wishlist

POST /api/v1/wishlist
  • Allowed to: only users.

Request body example:

{
    "productId": "650b02996dde3fe0155a2f5a"
}

Get logged user wishlist

GET /api/v1/wishlist
  • Allowed to: only users.

Remove product from wishlist

DELETE /api/v1/wishlist/:productId

-Allowed to: only users

Address endpoints

Add a new address to the logged user

POST /api/v1/address
  • Allowed to: only users

Request body example:

{
    "alias": "alias",
    "city": "City",
    "details": "address details",
    "phone": "010123456789",
    "postal code": "1234"
}

Get logged user addresses

GET /api/v1/address
  • Allowed to: only users.

Address from logged user addresses list

DELETE /api/v1/address/:addressId
  • Allowed to: only users

Cart endPoints

Add item to cart endpoint

POST /api/v1/cart
  • Allowed to: only users.

Request body example:

{
    "productId": "650b02996dde3fe0155a2f5a",
    "color": "red",
    "size": "XL"
}

Get logged user cart endpoint

GET /api/v1/cart
  • Allowed to: only users

Update specific item quantity endpoint

PUT /api/v1/cart/:itemId
  • Allowed to: only users

Request body example:

{
    "quantity": 4
}

Remove a specific item from the cart

DELETE /api/v1/cart/:itemId
  • Allowed to: only users

Clear user cart

DELETE /api/v1/cart
  • Allowed to: only users.

Apply coupon to the cart

POST /api/v1/cart/applyCoupon
  • Allowed to: only users

Request body example:

{
    "coupon": "name"
}

Coupon endpoints

Create a new coupon

POST /api/v1/coupons
  • Allowed to: only admins

Request body example:

{
    "name": "HAPPY_MOTHER'S_DAY",
    "expire": "09/25/2023",
    "discount": 25
}

Get all coupons

GET /api/v1/coupons
  • Allowed to: only admins

Update a specific coupon

PUT /api/v1/coupons/:id
  • Allowed to: only admins

Request body example:

{
    "name": "HAPPY_MOTHER'S_DAY",
    "expire": "09/25/2023",
    "discount": 25
}

Delete a specific coupon

DELETE /api/v1/coupons/:id
  • Allowed to: only admins.

Review endpoint

Nasted endpoints from products endpoints.

Add a new review

POST /api/v1/prodcut/:productId/reviews
  • Allowed to: only users.

Request body example:

{
    "title": "review title",
    "rating": 4
}

Get all reviews

GET /api/v1/prodcut/:productId/reviews
  • Open endpoint

Get a specific review

GET /api/v1/prodcut/:productId/reviews/:id
  • Open endpoint

Update a specific review

PUT /api/v1/prodcut/:productId/reviews/:id
  • Allowed to: Review owner (user)

Request body example:

{
    "title": "review title",
    "rating": 4
}

Delete a specific review

DELETE /api/v1/prodcut/:productId/reviews/:id
  • Allowed to: admins and review owner.

Order endpoints

Create a new cash order

POST /api/v1/orders/:cartId
  • Allowed to: only users

Request body example:

{
    "alias": "alias",
    "city": "City",
    "details": "address details",
    "phone": "010123456789",
    "postal code": "1234"
}

Get all orders

GET /api/v1/orders
  • Allowed to: only admins

GET a specific order

GET /api/v1/orders/:id
  • Allowed to: only admins

Get logged user all orders

GET /api/v1/orders
  • Allowed to: users.

GET a specific order for the logged user

GET /api/v1/orders/:id
  • Allowed: users

Update order delivery status

PUT /api/v1/orders/:id
  • Allowed to: only admins

Request body example

{
    "isDelivered": true
}

Update order paid status

PUT /api/v1/orders/:id
  • Allowed to: only admins

Request body example

{
    "isPaid": true
}

Create an online paid order

POST /api/v1/orders/checkout-session/:cartId
  • Allowed to: only users.

Request body example:

{
    "alias": "alias",
    "city": "City",
    "details": "address details",
    "phone": "010123456789",
    "postal code": "1234"
}

App settings endpoints

Add tax and shipment price

POST /api/v1/appSetting/taxAndShipmentPrices
  • Allowed to: only admins.

Request body example:

{
    "taxPrice": 15,
    "shipmentPrice": 30
}

note that only one tax price and one shipment price can be added.

Get tax and shipment prices

GET /api/v1/appSetting/taxAndShipmentPrices
  • Allowed to: only admins.

Update tax and shipment prices

PUT /api/v1/appSetting/taxAndShipmentPrices
  • Allowed to: only admins.

Delete tax and shipment prices

DELETE /api/v1/appSetting/taxAndShipmentPrices
  • Allowed to: only admins.

About

License:MIT License


Languages

Language:JavaScript 100.0%