Inventory Management API is a Node.js and Express backend for managing products, warehouses, stock records and inventory workflows.
The project focuses on realistic backend concerns such as authentication, role-based access control, controlled user provisioning, bulk inventory operations and stock movement history. It is built as a backend portfolio project and not as a full warehouse management system.
The goal of this project is to model a practical inventory backend with real business rules instead of only simple CRUD endpoints.
The API covers:
- product and warehouse management
- stock records for product/warehouse combinations
- goods receipt and goods issue workflows
- stock movement history
- role-based access control
- controlled user creation
- bulk operations for import and automation scenarios
The project is suitable for integration-focused use cases such as internal admin tools, warehouse workflows, automation processes or n8n-based inventory scenarios.
The API is deployed on Render and connected to MongoDB Atlas.
https://inventory-management-api-6zuo.onrender.com
Swagger UI is available at:
https://inventory-management-api-6zuo.onrender.com/api-docs
Health check:
https://inventory-management-api-6zuo.onrender.com/health
Note: The service runs on Render's free plan, so the first request after inactivity may take a few seconds.
- Node.js
- Express.js
- MongoDB
- Mongoose
- dotenv
- bcrypt
- jsonwebtoken
- express-validator
- helmet
- express-rate-limit
- swagger-jsdoc
- swagger-ui-express
- @apidevtools/swagger-parser
- Docker
- Docker Compose
- Jest
- Supertest
- mongodb-memory-server
- GitHub Actions
- Render
- MongoDB Atlas
- JWT-based login
- Login rate limiting for repeated failed login attempts
- Refresh token workflow
- Refresh token hashing and rotation
- Older refresh tokens are revoked after a new successful login under normal operation
- Logout with refresh token revocation
- Current user endpoint
- No public user registration
- Initial admin creation through seed script
- Admin-only user creation
- Admins can create
managerandviewerusers - Creating another admin through the API is not allowed
The API uses three roles:
| Role | Description |
|---|---|
admin |
Full access, including user creation and product deletion |
manager |
Can manage inventory data and inventory workflows |
viewer |
Read-only access to inventory data |
- Helmet-based security headers
X-Powered-Byheader disabled- Public Swagger UI for portfolio/demo visibility
- Protected API operations still require Bearer authentication
- Required startup environment validation
- Bounded MongoDB connection retry handling
- Production 5xx responses hide internal error details
- OpenAPI specification validation in the automated test suite
- Static validation for Docker, Docker Compose, Render and CI configuration
- Product management
- Warehouse management
- Stock records for product/warehouse combinations
- Goods receipt workflow for increasing stock
- Goods issue workflow for decreasing stock
- Goods receipt and goods issue reject inactive stock records
- Product and Warehouse inactivity is enforced by inventory workflows
- Legacy Product DELETE routes archive documents and preserve references
- Explicit Product, Warehouse and Stock aggregate versions
- Transactionally synchronized Stock lifecycle guards
- Stock movement parent snapshots, exact before/after quantities and aggregate versions
- Conditional stock updates to reduce normal overselling risk
- Transactional Stock and StockMovement persistence for inventory workflows
- Read-only stock movement history
- Bulk operations for products, warehouses, stocks and inventory workflows
Bulk endpoints are included for practical import and automation scenarios.
Supported bulk operations:
- bulk product creation
- bulk product update
- bulk Product archive through the deprecated DELETE compatibility route
- bulk warehouse creation
- bulk warehouse update
- bulk stock setup
- bulk goods receipt
- bulk goods issue
Bulk requests are limited to a maximum of 150 items per request.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/login |
Login and receive access/refresh tokens |
POST |
/api/auth/refresh |
Rotate refresh token and receive a new access token |
POST |
/api/auth/logout |
Revoke refresh token |
GET |
/api/auth/me |
Get the current authenticated user |
| Method | Endpoint | Role | Description |
|---|---|---|---|
POST |
/api/users |
admin | Create a manager or viewer user |
Public registration is intentionally not available. The first admin user is created through the seed script.
| Method | Endpoint | Role | Description |
|---|---|---|---|
GET |
/api/products |
admin, manager, viewer | Retrieve all products |
GET |
/api/products/:id |
admin, manager, viewer | Retrieve a single product |
POST |
/api/products |
admin, manager | Create a new product |
PATCH |
/api/products/:id |
admin, manager | Update product information |
PATCH |
/api/products/:id/deactivate |
admin, manager | Deactivate a product |
DELETE |
/api/products/:id |
admin | Archive an inactive product (deprecated alias) |
POST |
/api/products/bulk |
admin, manager | Create multiple products |
PATCH |
/api/products/bulk |
admin, manager | Update multiple products |
DELETE |
/api/products/bulk |
admin | Atomically archive inactive products (deprecated alias) |
| Method | Endpoint | Role | Description |
|---|---|---|---|
GET |
/api/warehouses |
admin, manager, viewer | Retrieve all warehouses |
GET |
/api/warehouses/:id |
admin, manager, viewer | Retrieve a single warehouse |
POST |
/api/warehouses |
admin, manager | Create a new warehouse |
PATCH |
/api/warehouses/:id |
admin, manager | Update warehouse information |
PATCH |
/api/warehouses/:id/deactivate |
admin, manager | Deactivate a warehouse |
POST |
/api/warehouses/bulk |
admin, manager | Create multiple warehouses |
PATCH |
/api/warehouses/bulk |
admin, manager | Update multiple warehouses |
Warehouse deletion is intentionally not implemented because warehouses can be connected to stock records and movement history.
| Method | Endpoint | Role | Description |
|---|---|---|---|
GET |
/api/stocks |
admin, manager, viewer | Retrieve all stock records |
GET |
/api/stocks/:id |
admin, manager, viewer | Retrieve a single stock record |
POST |
/api/stocks |
admin, manager | Create a stock record for a product and warehouse |
POST |
/api/stocks/bulk |
admin, manager | Create multiple stock records |
Stock quantity is not updated directly through the stock API. Quantity changes are handled through goods receipt and goods issue workflows.
| Method | Endpoint | Role | Description |
|---|---|---|---|
POST |
/api/goods-receipts |
admin, manager | Receive goods and increase stock quantity |
POST |
/api/goods-receipts/bulk |
admin, manager | Process multiple goods receipts |
| Method | Endpoint | Role | Description |
|---|---|---|---|
POST |
/api/goods-issues |
admin, manager | Issue goods and decrease stock quantity |
POST |
/api/goods-issues/bulk |
admin, manager | Process multiple goods issues |
| Method | Endpoint | Role | Description |
|---|---|---|---|
GET |
/api/stock-movements |
admin, manager, viewer | Retrieve stock movement history |
GET |
/api/stock-movements/:id |
admin, manager, viewer | Retrieve a single stock movement |
Stock movements are generated by inventory workflows and exposed as read-only history. Manual stock movement creation is intentionally not exposed.
Before users can log in, create the first admin user:
npm run seed:adminThe command uses these environment variables:
ADMIN_NAME=Initial Admin
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=change_this_admin_passwordThe seed command is safe to run again. If an admin already exists, no new admin will be created.
{
"email": "admin@example.com",
"password": "change_this_admin_password"
}{
"name": "Warehouse Manager",
"email": "manager@example.com",
"password": "ChangeMe_Strong_123!",
"role": "manager"
}{
"sku": "LAPTOP-001",
"name": "Dell Latitude 7450",
"description": "Business laptop",
"unit": "piece"
}[
{
"sku": "PROD-001",
"name": "Product One",
"unit": "piece"
},
{
"sku": "PROD-002",
"name": "Product Two",
"unit": "piece"
}
]{
"code": "WH-STU",
"name": "Main Warehouse",
"description": "Primary warehouse for incoming and outgoing goods"
}[
{
"code": "WH-001",
"name": "Main Warehouse"
},
{
"code": "WH-002",
"name": "Secondary Warehouse"
}
]{
"productId": "PRODUCT_ID",
"warehouseId": "WAREHOUSE_ID"
}[
{
"productId": "PRODUCT_ID",
"warehouseId": "WAREHOUSE_ID"
},
{
"productId": "ANOTHER_PRODUCT_ID",
"warehouseId": "WAREHOUSE_ID"
}
]{
"stockId": "STOCK_ID",
"quantity": 10,
"reference": "PO-1001",
"reason": "Supplier delivery"
}[
{
"stockId": "STOCK_ID",
"quantity": 10,
"reference": "PO-1001",
"reason": "Supplier delivery"
},
{
"stockId": "STOCK_ID",
"quantity": 5,
"reference": "PO-1002",
"reason": "Second delivery"
}
]{
"stockId": "STOCK_ID",
"quantity": 3,
"reference": "SO-1001",
"reason": "Customer order"
}[
{
"stockId": "STOCK_ID",
"quantity": 3,
"reference": "SO-1001",
"reason": "Customer order"
},
{
"stockId": "STOCK_ID",
"quantity": 2,
"reference": "SO-1002",
"reason": "Second order"
}
]{
"refreshToken": "REFRESH_TOKEN"
}{
"refreshToken": "REFRESH_TOKEN"
}- Public registration is not available.
- The first admin user is created through
npm run seed:adminor the Docker Compose seed profile. - The seed script checks whether an admin already exists before creating one.
- Admin users can create
managerandviewerusers. - API users cannot create another admin through
/api/users. - Passwords are hashed before being stored.
- Refresh tokens are stored as hashes in the database.
- Refresh tokens are rotated when refreshing access tokens.
- Older refresh tokens are revoked after a new successful login under normal operation.
- Logout revokes the submitted refresh token.
- Login requests are rate-limited after repeated failed attempts.
- Each product must have a unique SKU.
- Product SKUs are normalized to uppercase.
- Product SKUs use a restricted identifier format.
- Each product must have a name.
- Product unit is limited to predefined values.
- New products are active by default.
- Products can be updated partially.
- Product and Warehouse updates expose an explicit
version;__vis not the domain version. - Update/deactivate requests may supply an optional positive-integer
expectedVersion. - Products must be deactivated before the legacy DELETE route can archive them.
- Archive retains the Product, prevents SKU reuse, preserves Stock and movement references, and hides the Product from normal Product reads and updates.
- Archived Products are terminal in the current API and have no restore endpoint.
Supported product units:
piece
kg
liter
meter
- Each warehouse must have a unique code.
- Warehouse codes are stored in uppercase and use a restricted identifier format.
- Warehouse codes are treated as business identifiers.
- Warehouse codes cannot be changed through update endpoints.
- Warehouses can be deactivated.
- Inactive Warehouses remain readable and may be reactivated through PATCH.
- Warehouses are not deleted.
- A stock record connects one product with one warehouse.
- The combination of product and warehouse must be unique.
- A stock record can only be created for an active product.
- A stock record can only be created for an active warehouse.
- Stock quantity starts at
0. - Stock carries derived Product and Warehouse lifecycle guards plus an explicit aggregate
version. - Stock creation conditionally touches each distinct parent version inside its transaction; that increment represents the new aggregate relationship.
- Stock quantity is not changed directly through the stock API.
- Stock quantity changes are handled through goods receipt and goods issue workflows.
- Goods receipt creates a stock movement and increases current stock quantity.
- Goods issue creates a stock movement and decreases current stock quantity.
- Goods issue is rejected when available quantity is insufficient.
- Goods receipt and goods issue are rejected for inactive stock records.
- Goods receipt and goods issue also reject inactive/archived Products, inactive Warehouses, missing parent references, and unresolved lifecycle guards.
- Goods issue uses conditional stock updates to reduce normal concurrent overselling risk.
- Goods receipt and goods issue commit Stock and StockMovement changes atomically.
- Bulk goods receipt and issue requests are all-or-nothing transactions.
- Every original movement item increments Stock version once. Repeated Stock IDs in bulk receive sequential before/after quantities and sequential
aggregateVersionvalues in request order. - New movements include direct Product/Warehouse references and immutable
{sku,name}/{code,name}snapshots. - Stock movements are read-only history.
- Manual stock movement creation is intentionally not exposed.
Stock quantity is not changed directly.
Inventory changes are handled through business workflows:
- Goods receipt
- Goods issue
- Stock movement history
The stock model connects products and warehouses. A stock record represents the quantity of one product in one warehouse.
Product
|
v
+-------+
| Stock |
+-------+
^
|
Warehouse
Stock changes are handled by:
Goods Receipt -> increases stock -> creates stock movement
Goods Issue -> decreases stock -> creates stock movement
This keeps the current inventory state and its movement history separated.
The project follows a simple layered backend structure.
Client
|
v
Routes
|
v
Authentication / RBAC
|
v
Validation
|
v
Controllers
|
v
Application Services
|
v
Transaction Helper (Inventory Mutations)
|
v
Models
|
v
MongoDB
Errors
|
v
Global Error Handler
Authentication is handled through JWT access tokens and refresh tokens.
Access tokens are used to protect private routes. Refresh tokens are stored as hashes in the database and can be revoked during logout or token rotation.
The application also applies basic security hardening through Helmet, disables the X-Powered-By header and limits repeated failed login attempts.
For automated testing, the Express application is separated from the server startup logic. src/app.js exports the Express app for tests, while src/server.js connects to the database and starts the HTTP server.
Server startup validates required environment variables before the application starts. MongoDB connection handling includes bounded retry attempts before failing the process.
Routes define the API endpoints and forward requests to the correct controller.
Validation rules check request payloads before controller logic is executed.
Controllers handle HTTP input and output. Goods receipt, goods issue, and Stock setup write controllers delegate their business behavior to application services.
The Inventory and Stock application services accept plain JavaScript inputs, apply the existing write rules, and use the Mongoose models. They do not depend on Express request or response objects.
Typed domain errors represent expected service failures internally. The global error handler preserves the existing public status codes and message-only error responses.
Goods receipt and goods issue services use one MongoDB transaction per request. Stock changes and their StockMovement records commit together, and each bulk request is all-or-nothing. The previous manual compensation updates were removed.
Product and Warehouse mutation controllers delegate lifecycle, explicit version, compare-and-swap, archive, and Stock guard propagation behavior to dedicated application services. Parent lifecycle and related Stock guard changes commit in one transaction. Inventory transactions validate both authoritative parents and derived Stock guards, then write that same Stock aggregate so concurrent parent lifecycle changes conflict safely.
version is the authoritative domain aggregate revision; Mongoose __v is not
the API concurrency contract. expectedVersion is optional on the current
legacy Product/Warehouse mutation APIs, so clients that omit it can still have
last-write-wins behavior. Mandatory preconditions are deferred to an approved
versioned API contract.
Transactions provide atomic database persistence and driver retry safety. Inventory Core mutation routes additionally support optional, actor-scoped idempotency for retry-safe HTTP execution. AuditEvent, OutboxEvent, machine identity, and API versioning remain future work.
Every HTTP attempt receives a server-generated UUID in X-Request-ID. A valid
incoming X-Correlation-ID (1-128 characters matching
^[A-Za-z0-9._:-]+$) is propagated; when omitted it defaults to the request
ID. Invalid correlation values fail with 400 before authentication or mutation.
Both values live in an explicit plain application context. Authentication adds
only the current user actor type and ID; tokens, roles, names, and email are not
copied into persistence context.
All Product, Warehouse, Stock, Goods Receipt, and Goods Issue mutation routes
accept an optional Idempotency-Key. Keys are case-sensitive, 8-128 characters,
and use the same safe character set. The raw value is never stored: SHA-256 is
used in the unique actor/operation scope. The normalized business command is
hashed with canonical-json-v1, after validation and normalization.
The first successful keyed execution returns Idempotency-Replayed: false.
The same actor, operation, key, and normalized command later receives the exact
stored successful status/body with Idempotency-Replayed: true and fresh
request/correlation response headers. A changed command returns 409 and current
authentication/RBAC is evaluated before every replay. Only committed 2xx
responses are stored. Failures do not reserve a key.
Acquisition, the complete domain mutation, StockMovement writes, the response snapshot, and completion share one MongoDB transaction. MongoDB's unique scope index is the concurrency authority; there is no process-local or external lock, cleanup worker, or retry worker. Replay snapshots are limited to 1 MiB and are retained for seven days. MongoDB TTL deletion is asynchronous, so a record can remain authoritative slightly longer; the key becomes reusable after physical TTL removal.
Models define the MongoDB data structure using Mongoose schemas.
The config layer contains reusable configuration code, such as MongoDB and Swagger setup.
This structure keeps the project understandable and avoids unnecessary complexity.
Use .env.example as a template for local configuration:
cp .env.example .envOn Windows PowerShell:
Copy-Item .env.example .envExample values:
PORT=3000
MONGODB_URI=mongodb://localhost:27017/inventory_management?replicaSet=rs0&directConnection=true
JWT_ACCESS_SECRET=change_this_access_token_secret
JWT_ACCESS_EXPIRES_IN=15m
DB_CONNECT_RETRIES=2
DB_CONNECT_RETRY_DELAY_MS=1000
SWAGGER_PRODUCTION_URL=https://inventory-management-api-6zuo.onrender.com
ADMIN_NAME=Initial Admin
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=change_this_admin_passwordThe .env file is ignored by Git and should not be committed.
For production deployments, use strong secret values and do not commit real credentials.
This repository includes a short production data note in:
docs/production-data-notes.md
Before using an existing production database, the following data-related checks should be reviewed:
- existing mixed-case product SKUs
- existing stock movement types
- existing admin users
- MongoDB unique indexes
- legacy refresh token data
- production secret rotation
Work Package 3 requires the controlled lifecycle/version migration before enforcement traffic is enabled. The command is dry-run by default:
npm run migrate:phase1-lifecycleAfter reviewing invalid-version counts, orphan Stock reports, legacy movement
counts, and duplicate (stockId, aggregateVersion) candidates, apply explicitly:
npm run migrate:phase1-lifecycle -- --applyThe script uses MONGODB_URI, never runs at application startup, does not
delete data, and does not invent historical quantities, versions, or snapshots.
It backfills only safely derivable direct movement references and creates the
partial unique movement-version index only after duplicate preflight succeeds.
See docs/production-data-notes.md for deployment and rollback guidance.
These checks are operational deployment tasks and are not executed automatically by the application.
Install dependencies:
npm installCreate a local environment file:
cp .env.example .envCreate the initial admin user:
npm run seed:adminStart the development server:
npm run devThe server should start on:
http://localhost:3000
Run the automated test suite:
npm testBuild and start the API together with MongoDB:
docker compose up --buildCompose runs MongoDB 8 as a single-node replica set named rs0. Its health
check initializes the replica set when needed and waits for the node to become
primary before starting the API. The API container connects through:
mongodb://mongo:27017/inventory_management?replicaSet=rs0
When the application runs directly on the host against the Compose MongoDB, use:
mongodb://localhost:27017/inventory_management?replicaSet=rs0&directConnection=true
The existing mongo_data volume remains mounted and is not deleted by this
configuration. Do not use docker compose down -v when development data must
be retained.
The API will be available at:
http://localhost:3000
Health check endpoint:
http://localhost:3000/health
Swagger documentation:
http://localhost:3000/api-docs
To create the initial admin user inside the Docker Compose setup, run the seed service:
docker compose --profile seed run --rm seed-adminStop the containers:
docker compose downThe seed step is intentionally separated from normal application startup. It should be run only when an initial.
If the admin user has not been created yet, run the seed command with the correct environment variables:
npm run seed:adminThe seed step is not intended to run automatically on every application startup.
The project includes automated API, integration and configuration tests with Jest, Supertest and a transaction-capable MongoMemoryReplSet.
The tests cover:
- root API endpoint
- authentication workflows
- refresh token rotation
- logout token revocation
- login rate limiting
- rejection of public registration
- admin-only user creation
- role-based access control
- product API
- warehouse API
- stock API
- stock movement read routes
- goods receipt workflow
- goods issue workflow
- bulk operations
- request validation
- inactive stock rejection in inventory workflows
- inactive/archived Product and inactive Warehouse enforcement
- Product archive, reference preservation, lifecycle metadata and optimistic conflicts
- Stock lifecycle guard propagation and lifecycle-versus-inventory races
- explicit aggregate versions, transaction retry behavior and sequential bulk versions
- immutable StockMovement snapshots and historical direct references
- migration dry-run, apply, rerun, orphan, duplicate-index and connection-close behavior
- concurrent goods issue scenarios
- production error handling behavior
- MongoDB connection retry behavior
- required server environment variables
- public Swagger UI availability
- OpenAPI specification validation
- Docker, Docker Compose, Render and CI configuration checks
- disabled manual stock movement creation
Run tests:
npm testInteractive API documentation is available through Swagger UI.
Local Swagger UI:
http://localhost:3000/api-docs
Production Swagger UI:
https://inventory-management-api-6zuo.onrender.com/api-docs
inventory-management-api/
|
|-- .github/
| `-- workflows/
| `-- ci.yml
|
|-- scripts/
| |-- seedAdmin.js
| `-- migrations/
| `-- phase1LifecycleVersion.js
|
|-- src/
| |-- app.js
| |-- server.js
| |
| |-- config/
| | |-- database.js
| | `-- swagger.js
| |
| |-- errors/
| | |-- DomainError.js
| | `-- errorCodes.js
| |
| |-- controllers/
| | |-- authController.js
| | |-- userController.js
| | |-- productController.js
| | |-- warehouseController.js
| | |-- stockController.js
| | |-- stockMovementController.js
| | |-- goodsReceiptController.js
| | `-- goodsIssueController.js
| |
| |-- middleware/
| | |-- authMiddleware.js
| | |-- roleMiddleware.js
| | |-- validateRequest.js
| | |-- loginRateLimiter.js
| | `-- errorHandler.js
| |
| |-- models/
| | |-- User.js
| | |-- RefreshToken.js
| | |-- Product.js
| | |-- Warehouse.js
| | |-- Stock.js
| | `-- StockMovement.js
| |
| |-- services/
| | |-- inventoryService.js
| | |-- stockService.js
| | |-- productService.js
| | `-- warehouseService.js
| |
| |-- utils/
| | `-- transaction.js
| |
| |-- routes/
| | |-- authRoutes.js
| | |-- userRoutes.js
| | |-- productRoutes.js
| | |-- warehouseRoutes.js
| | |-- stockRoutes.js
| | |-- stockMovementRoutes.js
| | |-- goodsReceiptRoutes.js
| | `-- goodsIssueRoutes.js
| |
| `-- validators/
| |-- userValidator.js
| |-- productValidator.js
| |-- warehouseValidator.js
| |-- stockValidator.js
| |-- goodsReceiptValidator.js
| `-- goodsIssueValidator.js
|
|-- tests/
| |-- helpers/
| | `-- authTestHelper.js
| |
| |-- app.test.js
| |-- auth.test.js
| |-- database.test.js
| |-- deploymentConfig.test.js
| |-- errorHandler.test.js
| |-- inventoryService.test.js
| |-- inventoryIntegrity.test.js
| |-- inventoryWorkflow.test.js
| |-- lifecycleVersion.test.js
| |-- migration.test.js
| |-- product.test.js
| |-- server.test.js
| |-- stock.test.js
| |-- stockService.test.js
| |-- stockMovement.test.js
| |-- swagger.test.js
| |-- transaction.test.js
| |-- user.test.js
| |-- warehouse.test.js
| `-- setupTestDb.js
|
|-- docs/
| |-- Swagger-UI.png
| |-- architecture.md
| `-- production-data-notes.md
|
|-- .env.example
|-- .dockerignore
|-- .gitignore
|-- Dockerfile
|-- docker-compose.yml
|-- package.json
|-- package-lock.json
|-- README.md
`-- render.yaml
Implemented:
- product management API
- warehouse management API
- stock management API
- controlled admin seeding
- admin-only user creation
- JWT authentication
- refresh token rotation
- refresh token revocation
- login rate limiting
- role-based access control
- protected inventory endpoints
- product bulk operations
- warehouse bulk operations
- stock bulk setup
- goods receipt workflow
- goods issue workflow
- atomic Goods Receipt and Goods Issue transactions
- all-or-nothing bulk inventory mutations
- Product archive semantics with retained references and deprecated DELETE aliases
- Product/Warehouse lifecycle metadata and transactional Stock guard propagation
- explicit Product, Warehouse and Stock aggregate versions
- optional legacy
expectedVersioncompare-and-swap preconditions - enriched StockMovement historical context and sequential aggregate versions
- dry-run-first lifecycle/version migration with controlled partial unique index
- read-only stock movement history
- request validation with express-validator
- SKU and warehouse-code normalization rules
- security headers with Helmet
- production-safe 5xx error responses
- MongoDB connection retry handling
- global error handling
- Swagger / OpenAPI documentation
- formal OpenAPI validation in tests
- Docker support
- Docker Compose setup with MongoDB
- Docker Compose single-node MongoDB replica set
- Docker Compose seed profile for initial admin creation
- Render blueprint configuration
- automated API and configuration tests
- GitHub Actions CI workflow with Docker build step
- deployment on Render
- MongoDB Atlas production database connection
Current focus:
- production data verification
- deployment verification after push
- operational monitoring improvements
Possible future improvements:
- request-level audit metadata
- pagination and filtering for larger datasets
- improved operational logging
- production monitoring and alerting
- deployment smoke checks after Render release
- stronger admin recovery workflow
- database-level enforcement for one active admin if required
- future domain migrations beyond the lifecycle/version cutover
- distributed rate limiting for multi-instance deployments
- AuditEvent and OutboxEvent integration using the existing request context
Inventory and warehouse management are common real-world business problems. Companies need systems that can manage products, warehouses, stock levels, goods receipts, goods issues and movement history.
This project demonstrates backend skills that are relevant for roles such as:
- Backend Developer
- API Developer
- Integration Developer
- Software Developer
The project is focused on backend logic and API design. It shows how backend APIs can model real business rules instead of only simple CRUD operations.
The project follows a business-first approach.
Instead of updating inventory quantities directly, stock changes are handled through goods receipt and goods issue workflows. Each workflow updates the current stock quantity and creates stock movement history.
Some design choices are intentional:
- no public registration
- no automatic admin creation on application startup
- initial admin creation is an explicit setup step
- no direct stock quantity updates
- no manual stock movement creation
- stock movement history is generated by inventory workflows
- goods receipt and goods issue reject inactive stock records
- Product archive replaces supported runtime hard delete and preserves historical references
- inventory workflows require active Product, Warehouse, Stock, and synchronized lifecycle guards
- explicit
versionfields are domain revisions;__vremains Mongoose-internal - optional
expectedVersionis transitional and omission still permits last-write-wins - goods issue uses conditional stock updates to reduce normal overselling risk
- Inventory Core mutations use MongoDB transactions and support optional seven-day idempotent replay; callers that omit the header retain legacy behavior
- login rate limiting is process-local and suitable for this single-instance demo setup
- Swagger is public for portfolio/demo visibility, while protected endpoints still require authentication
- production data compatibility notes are documented separately in
docs/production-data-notes.md
ISC
This project is built as a backend portfolio project to demonstrate API design, authentication, RBAC, inventory workflows and integration-ready bulk operations.
