InvenTrack Backend Server is a web server for managing and retrieving information about various skincare and beauty products. Built using Express.js, it interacts with a database via Supabase, a scalable and powerful database client. The server includes robust error handling, improved security measures, and a well-structured API to ensure smooth operations.
- Node.js: Ensure you have the Node.js version specified in
package.json
. - Supabase Account: Obtain your API keys from Supabase.
Create a .env
file in the root directory with the following variables:
SUPABASE_URL
: Your Supabase URLSUPABASE_ANON_KEY
: Your Supabase anonymous keyPORT
: The port on which the server will run (default: 5000)MOXIE_WEBHOOK_SECRET_TOKEN
: Token for webhook authenticationSESSION_SECRET
: Secret for session managementNODE_ENV
: Set to 'production' for production environment
-
Clone the repository:
git clone https://github.com/yourusername/inventrack-backend.git
-
Install dependencies:
npm install
-
Configure environment variables: Ensure the
.env
file is correctly set up with the appropriate values. -
Start the server:
npm start
The backend will run on an Express server, listening on the specified port (default: 5000).
/api/user
: User authentication (sign-up, login, logout)/api/products
: Product-related operations/api/suppliers
: Supplier-related operations/api/webhook
: Webhook handling (currently for Moxie)/api/storage
: Profile image operations (upload, delete)
├── server.js # Entry point of the application
├── utils/ # Utility functions and helpers
│ ├── customErrors.js
│ ├── supabaseErrorHandler.js
│ └── generateSecureToken.js
├── controllers/ # Request handlers
│ ├── authController.js
│ ├── productsController.js
│ ├── profileController.js
│ ├── suppliersController.js
│ ├── webhookController.js
│ └── storageController.js # Handles profile image operations
├── middleware/ # Custom middleware functions
│ ├── index.js
│ ├── errorHandler.js
│ ├── corsOptions.js
│ ├── validateJWT.js
│ ├── validateRequest.js
│ └── checkMoxieToken.js
├── models/ # Data models and validation schemas
│ ├── userModel.js
│ ├── productModel.js
│ ├── supplierModel.js
│ ├── categoryModel.js
│ └── profileModel.js
├── routes/ # API route definitions
│ ├── authRoutes.js
│ ├── productsRoutes.js
│ ├── profilesRoutes.js
│ ├── suppliersRoutes.js
│ ├── webhookRoutes.js
│ └── storageRoutes.js # Routes for profile image operations
├── services/ # Business logic and data access
│ ├── productServices.js
│ ├── suppliersServices.js
│ └── storageService.js # Supabase Storage operations
├── config/ # Configuration files
│ └── supabaseClient.js
├── package.json # Project dependencies and scripts
└── .env # Environment variables (not in repo)
-
server.js
- The main entry point of the application.
- Sets up the Express server, applies middleware, and connects routes.
- Initializes the database connection (Supabase in this case).
-
utils/
- Contains utility functions that are used across the application.
- Examples:
generateSecureToken.js
for creating secure tokens,logger.js
for consistent logging. - These are general-purpose helpers that don't fit into other categories.
-
controllers/
- Houses the logic for handling requests and sending responses.
- Each file typically corresponds to a specific resource (e.g.,
productsController.js
,suppliersController.js
). - Controllers use services to perform business logic and data operations.
-
middleware/
- Custom middleware functions that can be applied to routes.
- Examples:
validateJWT.js
for authentication,validateRequest.js
for input validation. errorHandler.js
provides centralized error handling for the application.
-
models/
- Defines data structures and validation schemas for the application.
- Uses Joi for defining schemas (e.g.,
productModel.js
,supplierModel.js
). - Each model file exports schemas for both creation and update operations.
-
routes/
- Defines the API endpoints and maps them to controller functions.
- Organizes routes by resource (e.g.,
productsRoutes.js
,suppliersRoutes.js
). - Applies relevant middleware (like authentication and validation) to routes.
-
services/
- Contains the core business logic of the application.
- Handles data processing, interactions with the database (Supabase), and any complex operations.
- Keeps controllers lean by abstracting data manipulation and business rules.
-
config/
- Holds configuration files for different parts of the application.
supabaseClient.js
sets up the connection to the Supabase database.
- A request comes in and is routed by Express based on the definitions in the
routes/
folder. - The appropriate middleware in
middleware/
is applied (e.g., JWT validation, request validation). - The route handler in
controllers/
processes the request. - If needed, the controller calls functions from
services/
to perform business logic or data operations. - Services interact with the database using the Supabase client configured in
config/
. - Data is validated against schemas defined in
models/
. - The response is sent back to the client.
- Any errors are caught by the error handling middleware and appropriately responded to.
We've implemented Joi for robust data validation in our models. Each model (e.g., productModel.js
, supplierModel.js
) now includes:
- A main schema for full object validation
- An update schema for partial updates
- Validation functions for both creation and update operations
Example (Product Model):
const productSchema = Joi.object({
name: Joi.string().required().max(255).trim(),
retail_price_per_unit: Joi.number().min(0).required(),
selling_price_per_unit: Joi.number().min(0).required(),
quantity: Joi.number().integer().min(0).required(),
reorder_point: Joi.number().integer().min(0).required(),
// ... other fields
});
const productUpdateSchema = productSchema.fork(
Object.keys(productSchema.describe().keys),
(schema) => schema.optional()
);
Controllers have been updated to provide more informative responses and better error handling:
- Consistent use of JSON responses
- Specific error codes (e.g., 404 for not found)
- More detailed success messages
- Improved error logging
Example:
export const addProduct = async (req, res) => {
try {
const newProduct = req.body;
const addedProduct = await addNewProduct(req.supabase, newProduct);
res
.status(201)
.json({ message: "Product added successfully", product: addedProduct });
} catch (error) {
console.error("Error in addProduct:", error);
res
.status(500)
.json({ error: "An error occurred while adding the product" });
}
};
A new validateRequest
middleware has been added to handle request validation using Joi schemas:
export const validateRequest = (schema) => {
return (req, res, next) => {
const { error } = schema.validate(req.body, { abortEarly: false });
if (error) {
const errors = error.details.map((detail) => detail.message);
return res.status(400).json({ errors });
}
next();
};
};
This middleware is used in routes to validate incoming request bodies before they reach the controller.
- Separation of Concerns: Clear separation between models, controllers, services, and routes.
- Data Validation: Robust input validation using Joi schemas.
- Error Handling: Centralized error handling with informative responses.
- Code Organization: Modular structure with clear file naming conventions.
- Security: JWT authentication and role-based access control.
- Flexibility: Support for both full and partial updates in models and controllers.
- Consistent API Responses: Standardized JSON response format across all endpoints.
- Helmet: Implements various HTTP headers for security.
- Rate Limiting: Prevents abuse of the API.
- Session Management: Enhanced with secure cookies.
- Error Handling: Centralized error handling with detailed logging.
id
: UUID, Primary Key (managed by Supabase auth)email
: Email addresscreated_at
: Account creation date
id
: UUID, Primary Key (references auth.users.id)user_id
: UUID, Foreign Key to users tablefull_name
: Full namerole
: Role of the user (admin, manager, staff)cell_number
: Cell numberstore_name
: Name of the storecreated_at
: Profile creation dateupdated_at
: Profile last updated date
id
: BIGINT, Primary Key
name
: Name of the product - REQUIREDshort_description
: Short description of the productlong_description
: Long description of the productsku
: Stock Keeping Unit, uniqueretail_price_per_unit
: Retail price of the product - REQUIREDwholesale_price_per_unit
: Wholesale price of the product - REQUIREDtotal_quantity
: Total current quantity of the product - REQUIREDquantity_office_1
: Quantity in Office 1 - REQUIREDquantity_office_8
: Quantity in Office 8 - REQUIREDquantity_home
: Quantity at Home - REQUIREDdisplay_shelf
: Quantity on office shelf - REQUIREDreorder_point
: Minimum threshold quantity - REQUIREDcategory_id
: BIGINT, Foreign Key to categories tablesupplier_id
: BIGINT, Foreign Key to suppliers tablenote
: Additional notesstock_retail_value
: Total retail value of stock (calculated)stock_wholesale_value
: Total wholesale value of stock (calculated)image_url
: URL of the product imagemeasurement_unit
: Unit of measurementstatus
: Current status of the product (out, low, normal)created_at
: Creation dateupdated_at
: Last updated date
id
: BIGINT, Primary Keyname
: Name of the supplier - REQUIREDcontact_person
: Contact person's nameemail
: Email addressphone
: Phone numberaddress
: Address of the suppliercreated_at
: Creation dateupdated_at
: Last updated date
id
: BIGINT, Primary Keytype
: Type of category (retail or service) - REQUIREDname
: Name of the categorycreated_at
: Creation dateupdated_at
: Last updated date
id
: BIGINT, Primary Keyproduct_id
: BIGINT, Foreign Key to products tableaction
: Type of action performed - REQUIREDquantity_change
: Change in quantityprice_change
: Change in priceperformed_by
: Foreign Key to Profilescreated_at
: Timestamp of the action
id
: BIGINT, Primary Keyuser_id
: UUID, Foreign Key to auth.users tableproduct_id
: BIGINT, Foreign Key to products tablemessage
: TEXT, Notification message - REQUIREDis_read
: BOOLEAN, Indicates if the notification has been readcreated_at
: TIMESTAMPTZ, Timestamp of notification creation
- storageController.js: Handles the logic for uploading and retrieving profile images.
- storageService.js: Contains the core functions for interacting with Supabase Storage.
- storageRoutes.js: Defines the API endpoints for profile image operations.
- POST
/api/storage/:userId/profile-image
: Upload a profile image - GET
/api/storage/:userId/profile-image
: Retrieve a profile image URL
- JWT-based Authentication: Provides secure authentication.
- User Roles: Roles like admin, manager, and staff are defined for access control.
- Profile image: operations require authentication. The system uses cookie-based authentication with an
authToken
cookie.
- Centralized Error Handling: Middleware with detailed logging.
- Custom Error Types: Includes scenarios like ValidationError, UnauthorizedError.
- Environment-based Responses: Detailed in development, generalized in production.
To run tests (if implemented):
npm test
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE.md file for details.