This project is a Back-end RESTful API for managing a store. It enables the creation, listing, updating and deletion (CRUD) of products and sales. I also wrote tests for the API.
In this project I was able to apply the concepts of software architecture based on layers. Three layers are used: Model, Service and Controller (MSC). The Model is responsible for communicating with the database, the middle layer, Service, validates the business rules and the Controller receives and responds HTTP requests.
This project was developed while studying Back-end web development @betrybe. The files I worked on are in the /src
and /tests
folders. I got approval on 100% of this project's requirements.
Below is the database diagram:
- Node.js
- Express.js
- MySQL
- Joi for input data validation
- Tests with Mocha, Chai and Sinon
- Docker
- Layered Software Architecture
With Docker
- Start the
store_manager
andstore_manager_db
containers with thedocker-compose up -d
command - Access the
store_manager
container terminal withdocker exec -it store_manager bash
- In the terminal, install the dependencies with
npm install
- All other node commands must be run inside the container
Without Docker
- Install the dependencies with
npm install
(requires node on version 16) - Configure a
.env
file based on the.env.example
avaliable.
Commands
- Run the app with
npm start
ornpm run debug
(live reload) - Use
npm run migration
to create the database and entities andnpm run seed
to populate it - To run the project's requirements tests, first start the app with
npm run dev
, thennpm test
for all tests ornpm test <test-name>
for a specific requirement (ex.npm test req01
) - User
npm run test:mocha
to run the tests done by me
GET /products
- Returns an array with all the registered products ordered by their id, or an empty array if there are no products.
- Example:
[
{
"id": 1,
"name": "Martelo de Thor"
},
{
"id": 2,
"name": "Traje de encolhimento"
}
]
GET /products/:id
- Returns the product with the specified
id
. If there are no matches, returns status 404 with a message.
- Example of match:
{
"id": 1,
"name": "Martelo de Thor"
}
- Example of no match:
{ "message": "Product not found" }
POST /products
- Creates a new product in the
products
table and returns it with the inserted id. Two validations are done: (1) the product needs a name and (2) the name must be at least 5 characters long. If the new entry fails any of the validations, a message is returned instead.
- Example request body:
{
"name": "ProdutoX"
}
- Example of response for valid entry:
{
"id": 4,
"name": "ProdutoX"
}
- Response for request without a "name" field (status 400):
{ "message": "\"name\" is required" }
- Response for request with an invalid "name" (status 422):
{ "message": "\"name\" length must be at least 5 characters long" }
PUT /products/:id
- Updates a product and returns it with the respective id. The same validations of the product creation are done.
- Example request body:
{
"name": "Martelo do Batman"
}
- Example of return:
{
"id": 1,
"name": "Martelo do Batman"
}
- Invalid id (status 404):
{ "message": "Product not found" }
DELETE /products/:id
- Deletes a product and returns status 204. Validates if product exists.
GET /products/search?q=searchTerm
- Returns an array of products whose names matches the request search term. If there are no matches, returns an empty array. If the search term is empty, returns an array with all registered products.
- Example of match:
/products/search?q=Martelo
[
{
"id": 1,
"name": "Martelo de Thor"
}
]
GET /sales
- Returns an array with all the registered sales ordered by their saleId and productId, or an empty array if there are no sales.
- Example:
[
{
"saleId": 1,
"date": "2021-09-09T04:54:29.000Z",
"productId": 1,
"quantity": 2
},
{
"saleId": 1,
"date": "2021-09-09T04:54:54.000Z",
"productId": 2,
"quantity": 2
}
]
GET /sales/:id
- Returns the sale with the specified
id
, ordered by the productId. If there are no matches, returns status 404 with a message.
- Example of match:
[
{
"date": "2021-09-09T04:54:29.000Z",
"productId": 1,
"quantity": 2
},
{
"date": "2021-09-09T04:54:54.000Z",
"productId": 2,
"quantity": 2
}
]
- Example of no match:
{ "message": "Sale not found" }
POST /sales
- Inserts a new sale in the
sales
andsales_products
tables. The sale can be of one or many products. Four validations are done: (1) the "productdId" field is required; (2) the "quantity" field is required; (3) the "quantity" is greater than zero; (4) the "productId" corresponds to a registered product in the database.
- Example request body:
[
{
"productId": 1,
"quantity": 1
},
{
"productId": 2,
"quantity": 5
}
]
- Example of response for valid entry:
{
"id": 3,
"itemsSold": [
{
"productId": 1,
"quantity": 1
},
{
"productId": 2,
"quantity": 5
}
]
}
- Response for request without a "productId" field (status 400):
{ "message": "\"productId\" is required" }
- Response for request without a "quantity" field (status 400):
{ "message": "\"quantity\" is required" }
- Response for request with an invalid "productId" (status 404):
{ "message": "Product not found" }
- Response for request with an invalid "quantity" (status 422):
{ "message": "\"quantity\" must be greater than or equal to 1" }
PUT /sales/:id
- Updates a sale and returns it with the respective id. The same validations of the sale creation are done.
- Example request body:
[
{
"productId": 1,
"quantity": 10
},
{
"productId": 2,
"quantity": 50
}
]
- Example of return:
"saleId": 1,
"itemsUpdated": [
{
"productId": 1,
"quantity":10
},
{
"productId": 2,
"quantity":50
}
]
- Invalid id (status 404):
{ "message": "Sale not found" }
DELETE /sales/:id
- Deletes a sale and returns status 204. Validates if sale exists.