- Key feature
- Installation
- Schemas
- Endpoints
- Auth endpoints
- User endpoints
- Create new user endpoint
- Get all user endpoint
- Get a specific user endpoint
- Update a specific user endpoint
- Update a specific user password endpoint
- Delete a specific user endpoint
- Get logged user data endpoint
- Update logged user data endpoint
- Update logged user password end point
- Delete logged user endpoint
- Categories endpoints
- Subcategories endpoints
- Brands endpoints
- Products endpoinsts
- Wishlist endpoints
- Addresses endpoints
- Cart endpoints
- Coupons endpoints
- Reviews endpoints
- Order endpoints
- App settings endpoints
- 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.
- 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.
- User wishlist.
- User cart.
- User addresses.
- Online orders.
- Cash orders.
- Product review and rating.
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.
{
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,
},
}
{
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,
},
}
{
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,
}
}
{
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,
},
}
{
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,
}
{
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"],
},
}
{
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.
{
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"],
},
}
{
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.
{
taxPrice: {
type: Number,
required: true,
},
shipmentPrice: {
type: Number,
required: true,
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: true,
},
}
POST /api/v1/auth/signup
- Open endpoint.
- Request body type: form-data.
Request body example:
Key | Value |
---|---|
name | new user |
avatar | user image |
user@email.com | |
password | new password |
passwordConfirmation | new pawword |
GET /api/v1/auth/verify/:token
note that an email verification will be sent to the user's email address.
POST /api/v1/auth/login
- Open endpoint.
Request body example:
{
"email": "user@email.com",
"password": "new password"
}
POST /api/v1/auth/forgotPassword
- Open endpoint
Request body example:
{
"email": "user@email.com"
}
POST /api/v1/auth/verifyResetCode
- Open endpoint
Request body example:
{
"resetCode": 123456
}
POST /api/v1/auth/resetPassword
- Open endpoint
Request body example:
{
"email": "user@email.com",
"password": "new password"
"passwordConfirmation": "new password"
}
POST /api/v1/users
- Allowed to: only admins.
- Request body type: form-data
Request body example:
Key | Value |
---|---|
name | new admin |
admin@email.com | |
phone | 010123456789 |
avatar | user image |
role | admin |
password | new password |
passwordConfirmation | new password |
emailVerified | true |
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 /api/v1/users/:id
- Allowed to: only admins
PUT /api/v1/users/:id
- Allowed to: only admins
- Request body type: form-data
Request body example:
Key | Value |
---|---|
name | updated admin |
admin@email.com | |
phone | 010123456789 |
avatar | user image |
role | admin |
emailVerified | true |
PUT /api/v1/users/changePassword/:id
- Allowed to: only admins
Request body example:
{
"currentPassword": "password",
"newPassword": "updated password",
"passwordConfirmation": "updated password"
}
DELETE /api/v1/users/:id
- Allowed to: only admins
GET /api/v1/users/getLoggedUser
- Allowed to: users and admins
PUT /api/v1/user/updateLoggedUserData
- Allowed to: users and admins
- Request body type: form-data
Request body example:
Key | Value |
---|---|
name | admin |
user@email.com | |
phone | 010123456789 |
avatar | user image |
PUT /api/v1/user/updateLoggedUserPassword
- Allowed to: users and admins
Request body example:
{
"currentPassword": "password",
"newPassword": "updated password",
"passwordConfirmation": "updated password"
}
DELETE /api/v1/user/deleteLoggedUser
- Allowed to: users and admins
POST /api/v1/category
- Allowed to: only admins.
- Type: form data.
Request body example:
Key | Value |
---|---|
name | new category |
image | Category image |
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 /api/v1/categories/:id
- Open endpoint.
- No authentication is required for this endpoint call.
PUT /api/v1/categories/:id
- Allowed to: only admins.
- Type: form data.
Request body example:
Key | Value |
---|---|
name | Category name |
image | Category image |
DELETE /api/v1/categories/:id
- Allowed to: only admins.
Nested routes from category routes
POST /api/v1/categories/:categoryId/subcategories
- Allowed to: only admins
Request body example:
{
"name": "new subcategory"
}
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 /api/v1/subcategories/:id
- Open endpoint.
- No authentication is required for this endpoint call.
PUT /api/v1/subcategories/:id
- Allowed to: only admins
Request body example:
{
"name": "updated subcategory"
}
DELETE /api/v1/subcategories/:id
- Allowed to: only admins
POST /api/v1/brands
- Allowed to: only admins.
- Type: form data.
Request body example:
Key | Value |
---|---|
name | Brand name |
image | Brand image |
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 /api/v1/brand/:id
- Open endpoint.
- No authentication is required for this API call.
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 /api/v1/brands/:id
- Allowed to: only admins
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 /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 /api/v1/products/:id
- Open endpoint.
- No authentication is required for this endpoint call.
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 /api/v1/products/:id
- Allowed to: only admins.
POST /api/v1/wishlist
- Allowed to: only users.
Request body example:
{
"productId": "650b02996dde3fe0155a2f5a"
}
GET /api/v1/wishlist
- Allowed to: only users.
DELETE /api/v1/wishlist/:productId
-Allowed to: only users
POST /api/v1/address
- Allowed to: only users
Request body example:
{
"alias": "alias",
"city": "City",
"details": "address details",
"phone": "010123456789",
"postal code": "1234"
}
GET /api/v1/address
- Allowed to: only users.
DELETE /api/v1/address/:addressId
- Allowed to: only users
POST /api/v1/cart
- Allowed to: only users.
Request body example:
{
"productId": "650b02996dde3fe0155a2f5a",
"color": "red",
"size": "XL"
}
GET /api/v1/cart
- Allowed to: only users
PUT /api/v1/cart/:itemId
- Allowed to: only users
Request body example:
{
"quantity": 4
}
DELETE /api/v1/cart/:itemId
- Allowed to: only users
DELETE /api/v1/cart
- Allowed to: only users.
POST /api/v1/cart/applyCoupon
- Allowed to: only users
Request body example:
{
"coupon": "name"
}
POST /api/v1/coupons
- Allowed to: only admins
Request body example:
{
"name": "HAPPY_MOTHER'S_DAY",
"expire": "09/25/2023",
"discount": 25
}
GET /api/v1/coupons
- Allowed to: only admins
PUT /api/v1/coupons/:id
- Allowed to: only admins
Request body example:
{
"name": "HAPPY_MOTHER'S_DAY",
"expire": "09/25/2023",
"discount": 25
}
DELETE /api/v1/coupons/:id
- Allowed to: only admins.
Nasted endpoints from products endpoints.
POST /api/v1/prodcut/:productId/reviews
- Allowed to: only users.
Request body example:
{
"title": "review title",
"rating": 4
}
GET /api/v1/prodcut/:productId/reviews
- Open endpoint
GET /api/v1/prodcut/:productId/reviews/:id
- Open endpoint
PUT /api/v1/prodcut/:productId/reviews/:id
- Allowed to: Review owner (user)
Request body example:
{
"title": "review title",
"rating": 4
}
DELETE /api/v1/prodcut/:productId/reviews/:id
- Allowed to: admins and review owner.
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 /api/v1/orders
- Allowed to: only admins
GET /api/v1/orders/:id
- Allowed to: only admins
GET /api/v1/orders
- Allowed to: users.
GET /api/v1/orders/:id
- Allowed: users
PUT /api/v1/orders/:id
- Allowed to: only admins
Request body example
{
"isDelivered": true
}
PUT /api/v1/orders/:id
- Allowed to: only admins
Request body example
{
"isPaid": true
}
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"
}
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 /api/v1/appSetting/taxAndShipmentPrices
- Allowed to: only admins.
PUT /api/v1/appSetting/taxAndShipmentPrices
- Allowed to: only admins.
DELETE /api/v1/appSetting/taxAndShipmentPrices
- Allowed to: only admins.