ColaCo
Summary
This monorepo hosts a full-stack application that can build a soda machine application. The client dispenses soda jsons from a virtual soda machine and the server hosts an api where an owner can manage their machine through an interface. The application utililzes the MVC framework as its architecture.
To follow along with the below steps, be sure to use the stack
branch. The main branch here is dedicated to the demo site running in Github Pages
Table of Contents
Photos
Client
API Interface
Installation
Before running this application, please ensure that your device has the latest version of NodeJS
Start
Quickstart
To start this application, run:
npm run quickstart
This will install all the dependencies for the application then concurrently begin the client and api development servers. By default, the client will be hosted on http://localhost:3000, the api interface on http://localhost:3001, and the api on http://localhost:4000. The database should also be seeded by this point as well
Client
Summary
The client
of this project is a React application that displays an SVG soda machine that contains a list of sodas retrieved from the server
through an api call.
User Actions
Purchase Soda
On the client
, a user can purchase sodas from the vending machine by clicking the button. Upon clicking the button, an api call will be sent to the server requesting to purchase a soda. Upon successful purchase, the browser will trigger the download of soda.json
which will contain the id
, label
and description
of the soda. In addition to the download, a message will appear on the side of the machine stating the soda label purchased.
API calls used
- GET
/api/sodas
: retrieves the list of sodas available from the database - PUT
/api/sodas
: decrements the type of soda purchased then returns the soda information to download asjson
Stretch Goals
- Utilize polling through useInterval to fetch new content over time
- Utilize React
lazy
,Suspense
to take advantage of code splitting and allow the client to load components as needed with webpack - Utilize React
Error Boundary
to catch JavaScript errors and display fallback UI - Add additional styling with SVGs or
Three.js
to page in order to keep users more engaged.
Server
Summary
The server
is a NodeJS application utilizing the web framework Express to host an API
for the client
. To simplify the managment of the soda machine, an interface
built with React was used to interact with the API
.
Interface
Summary
The interface displays information to the admin that includes the Soda listings
, Transactions placed
, and Revenue earned
. The admin additionally can interact with the API
directly here.
User Actions
Create Soda
An admin can create a soda by clicking on the Add Soda
button. This will bring up a modal that will request input from the admin to create the soda. On submit, a POST request will be sent to /api/sodas
and will respond with the soda created
Update Soda
An admin can update a soda utilizing the same modal by clicking the Update Soda
button. On sumbit, a PUT request will be sent to /api/sodas/:id
and will respond with how many rows were updated.
Delete Soda
An admin can delete a soda by clicking the Update Soda
button then on the modal click the Delete Soda
button. This will send a DELETE request to /api/sodas:id
and will respond with the result of the deletion
API calls used
- GET
/api/sodas
to retrieve all sodas from the database - GET
/api/transactions/
to retrieve all transactions from the database - GET
/api/revenue
to retrieve the current revenue of the soda machine - POST
/api/sodas
to add a soda to the database - PUT
/api/sodas/:id
to update a soda - DELETE
/api/sodas/:id
to delete a soda inthe database
Stretch Goals
- Include authentication to only allow soda machine admins to access the API. Either through a environment variable as a password or by creating admins in the database.
- Include a timeline of the soda machine revenue to analyze the most profitable times.
- Include a table of most purchased sodas to anaylze which sodas were the most popular
- Include pagination to our transaction and soda tables to allow admin to not have to request such a large amount of data from the server at one time.
- Create more tests to ensure quality of code.
API
Summary
The API is built with the Express web framework and Sequelize ORM. It is structured as follows
- controllers
- db
- models
- routes
- tests
- testUtils
- utils
app.js
server.js
The API is structured to modularize the code and allow testing on individual files. An example of testing an express app can be found here
Available Routes
Title | Get Sodas
|
Method | GET |
URL | /api/sodas
|
URL Parameters | None
|
Success Response | Code: 200 Content: [{id: '1', label: 'Pop', description: 'A soda', price: 1.00, quantity: 100 }, ...]
|
Error Response | Code: 400 BAD REQUEST Content: None
|
Sample Request |
curl http://localhost:4000/api/sodas
|
Notes | Returns a list of sodas |
Title | Get Transactions
|
Method | GET |
URL | /api/transactions
|
URL Parameters | None
|
Success Response | Code: 200 Content: [{id: '1', label: 'Pop', price: 1.00, time: 12/18/2021, 3:02:21 PM }, ...]
|
Error Response | Code: 400 BAD REQUEST ERROR Content: None
|
Sample Request |
curl http://localhost:4000/api/transactions
|
Notes | Returns a list of transactions |
Title | Get Revenue
|
Method | GET |
URL | /api/revenue
|
URL Parameters | None
|
Success Response | Code: 200 Content: [{revenue: 9.00}]
|
Error Response | Code: 400 BAD REQUEST Content: None
|
Sample Request |
curl http://localhost:4000/api/revenue
|
Notes | Returns the revenue of the soda machine |
Title | Add Soda
|
Method | POST |
URL | /api/sodas
|
URL Parameters | Required:{label: [String], price: [Float], description: [String], quantity: [Integer]}
|
Success Response | Code: 200 Content: {id: 1, label: 'Pop', price: 1.00, description: 'A soda', quantity: 100}
|
Error Response | Code:400 BAD REQUEST Content: None
|
Sample Request |
curl -X POST -H "Content-Type: application/json" -d '{"label":"pop","price":1.00,"description":"niceSoda", "quantity":100}' http://localhost:4000/api/sodas
|
Notes | Adds a soda |
Title | Buy Soda
|
Method | PUT |
URL | /api/sodas
|
URL Parameters | Required:{id: [id]}
|
Success Response | Code: 200 Content: [1,0]
|
Error Response | Code: 400 BAD REQUEST Content: {"error":"No more soda to dispense"}
|
Sample Request |
curl -X PUT -H "Content-Type: application/json" -d '{"id": [id]}' http://localhost:4000/api/sodas
|
Notes | Decrements the soda quantity |
Title | Update Soda
|
Method | PUT |
URL | /api/sodas/:id
|
URL Parameters | Required:{id: [id], label: [String], price: [Float], description: [String], quantity: [Integer]}
|
Success Response | Code: 200 Content: [1,0]
|
Error Response | Code: 400 BAD REQUEST Content: None
|
Sample Request |
curl -X PUT -H "Content-Type: application/json" -d '{"label":"Poppy","price":1.50,"description":"A new Soda", "quantity":150"}' http://localhost:4000/api/sodas/[id]
|
Notes | Updates a soda |
Title | Delete Soda
|
Method | DELETE |
URL | /api/sodas/:id
|
URL Parameters | Required:{id: [id]}
|
Success Response | Code: 200 Content: [1]
|
Error Response | Code: 400 BAD REQUEST Content: None
|
Sample Request |
curl -X DELETE http://localhost:4000/api/sodas/[id]
|
Notes | Deletes a Soda |
Database
For development of this application, SQLite was used due to its flexibility as a zero-configuration
and self-contained
database engine.
In production, it is recommended to utilize a SQL Database engines such as MySQL, PostGreSQL, etc. For my deployment of this application, I utilized MySQL
through the provisioning of JAWSDB
on Heroku
SQL vs NoSQL
In the development of this project, a SQL Database was utilized as the main platform for hosting the data. In hindsight, I believe that a NoSQL database would have been the correct choice due to the flexibility of data models and because my application does not require any relational mapping of the tables.
Models
This application relies on the Data Models of Soda
and Transaction
.
Soda {
label: String,
price: Number,
description: String,
quantity: Number
}
Transaction {
label: String,
price: Number
}
The models are then created as instances via Sequelize
Stretch Goals
- In the API for purchasing a soda, the api is sending back an object that is a representation of the soda purchased. The client then creates an
a
tag to initiate the functiondownloadJSON
to download the JSON file on the client. This method utilizes more resources on the browser and may stress mobile clients. It would be more appropriate to download from the server itself utilizing the methodres.download
- Convert database solution to utilize
mongoose
package since current application does not rely extensibly on relational mapping and it would be beneficial to have a more flexible data model. - Transfer application to a Docker container to make it an executable that can run in any environment
- Configure cors options to only accept origins for
client
- Create more tests to ensure quality of code.
Production
To deploy this application I utilized Heroku since they provide add-ons for production SQL databases such as JAWSDB that can easily be placed into the API
. If no JAWSDB
Configuration
The Client
during development utilizes the Server
endpont http://localhost:4000
for requests. For deployment, ensure that you set the .env variable REACT_APP_BASE_URL
to your production server. On the Heroku application dashboard, this can be found in settings
under the section Config Vars
.
Deployment
To deploy this application, be sure to have the Heroku CLI installed. Since this project is a monorepo, we will deploy the client and server individually as their own applications via Git Subtrees
Client
- Create a Heroku application
heroku create <optional-name>
- Utilizing the project endpoint, create a new git remote through this command
git remote add client <client-heroku-endpoint>
- Push the project the
client
remote via
npm run deploy-client
Server
- Create a Heroku application
heroku create <optional-name>
- Utilizing the project endpoint, create a new git remote through this command
git remote add server <server-heroku-endpoint>
- Push the project the
server
remote via
npm run deploy-server
Available Scripts
npm run quickstart - runs install script then start script
npm run install - install dependencies in application
npm start - runs seed script and develop script
npm run develop - starts client and api development servers
npm run client - starts client development server
npm run server - starts server api and interface servers
npm run seed - seeds sqlite database
npm run deploy:client - deploys client build to heroku endpoint
npm run deploy:server - deploys server build to heroku endpoint
npm run lint - runs linter on client and server
npm run test - runs tests on client and server
npm run prepare - prepares git hooks with husky
npm run build - runs build scripts of client and api-interface
Technologies Used
- React: JavaScript Framework for UI Interfaces
- Sequelize: Object Relational Mapper for SQL
- NodeJS: JavaScript Server Runtime Environment
- Express: JavaScript Web Framework
- Heroku: Cloud Application Platform to applications
- ESLint: JavaScript Linter to identify issues and conform style
- Husky: NPM package to run project scripts with Git Hooks
- Axios: promise based HTTP client for the Browser and NodeJS
- CORS: NPM package to enable Cross Origin Resource Sharing on the server.
Project Stretch Goals
- Utilize Continuous Integration in the project for future codebase development.
- Several eslint configs have rules turned off, turn them on in the future