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
.envfile 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.jsfor creating secure tokens,logger.jsfor 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.jsfor authentication,validateRequest.jsfor input validation. errorHandler.jsprovides 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.jssets 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
authTokencookie.
- 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.