This backend service entails constructing a Node.js/Express.js application designed to function as a REST API. The implementation addressed concepts such as race conditions and database transactions. Sequelize is utilized for the schema definition and DB interactions.
Profiles in this system can be categorized as either a client
or a contractor
. Clients have the ability to create contracts with contractors. Contractors, in turn, perform jobs for clients and receive payment. Each profile, whether client or contractor, possesses a 'balance' property.
In the relationship between a client and a contractor, contracts play a central role. These contracts can exist in one of three statuses: new
, in_progress
, or terminated
. Contracts are deemed active only when they are in the in_progress status. Additionally, contracts serve as containers grouping individual jobs within them.
Contractors receive payment for the jobs they perform for clients within a specific contract.
Below is a list of the API's for the application.
-
GET
/contracts/:id
- The endpoint should return the contract only if it is associated with the profile making the request. -
GET
/contracts
- The query should provide a list of contracts associated with a user (either a client or contractor). The list should exclusively include contracts that are currently active and not terminated. -
GET
/jobs/unpaid
- Get all unpaid jobs for a user (either a client or contractor), for active contracts only. -
POST
/jobs/:job_id/pay
- To pay for a job, a client is only eligible to proceed if their balance is greater than or equal to the specified payment amount. If the conditions are met, the designated amount should be transferred from the client's balance to the contractor's balance. -
POST
/balances/deposit/:userId
- Money can be deposited into the balance of a client. However, a client is restricted from depositing an amount that exceeds 25% of their total outstanding payments for jobs at the time of the deposit. -
GET
/admin/best-profession?start=<date>&end=<date>
- The endpoint should return the profession that earned the most money, calculated as the sum of payments for jobs, among all contractors who worked within the specified time range. -
GET
/admin/best-clients?start=<date>&end=<date>&limit=<integer>
- The endpoint should return the clients who paid the most for jobs within the specified time period. The query should also include a limit parameter, with the default limit set to 2.
[
{
"id": 1,
"fullName": "Reece Moyer",
"paid" : 100.3
},
{
"id": 200,
"fullName": "Debora Martin",
"paid" : 99
},
{
"id": 22,
"fullName": "Debora Martin",
"paid" : 21
}
]
All models are defined in src/model.js
-
Next,
npm run seed
will seed the local SQLite database. Warning: This will drop the database if it exists. The database lives in a local filedatabase.sqlite3
. -
Then run
npm start
which should start both the server and the React client.
-
The server is running with nodemon which will automatically restart for you when you modify and save a file.
-
The database provider is SQLite, which will store data in a file local to your repository called
database.sqlite3
. The ORM Sequelize is on top of it. You should only have to interact with Sequelize - please spend some time reading sequelize documentation before starting the exercise. -
To authenticate users use the
getProfile
middleware that is located under src/middleware/getProfile.js. users are authenticated by passingprofile_id
in the request header. after a user is authenticated his profile will be available underreq.profile
. -
The server is running on port 3001.