- Started with express-generate and removed as much of the unnecessary scaffolding as I could.
- Implemented all necessary scripts in package.json to test, seed the db and deploy
- Added my own custom database connection, seed, and seed-helper functions to populate the database efficiently with the test data.
- Added the model for the app
- Added
CircleCI
andCodeClimate
configurations to automate the testing of the project. - Created the endpoint and set up the manual pagination.
- Added as many test cases as I could think of, some boundary tests and tested the examples provided in the instructions.
- Deployed to Heroku and set up a persistent MongoDB Database on Atlas
From the challenge description it seemed that this implementation had a few unique characteristics:
- It had a nested query string, some options include a stringified Object form, bracket notated nested query params (e.g. range[by]) and a dot notated set of nested query params (e.g. range.by)
- The pagination could start based on finding matches for the start value, not just an index value.
For the first I chose to write my own query parameter parser that would create an object based on a given key prefix, for example ?range.by=id&range.start=5
would become {"range": {"by": "id", "start"=5}}
For the second I created a helper function searchArrayByProperty
which attempted to find the matches given by the query string start/end parameters within the array.
Finally for the more traditional sorting I chose to use a switch statement which incorporated ascending and descending functions for both primitive data types we expected. This was so our sort/reverse could run in O(n log n)
instead of adding another O(n)
to the runtime complexity.
I chose to interpret the query as a nested query string in the form of ?key.subkey=value
.
This way I was able to parse the query parameters for the key and store all the subkey values in an object.
Another way of doing this would be to have the query parameter be called range and JSON.stringify
an object with all the required key/value pairs then on the backend use JSON.parse
. This would be easier on the backend but harder to type into the browser address bar, although it could be easier to send from the frontend as an object. I chose to have it easier to type in the browser address bar since it would be easier to quickly test manually.
- non-determinism: There is an inherent issue with using
by=name&start=my-app-001
: we are lucky that they match up directly with their inherent index, however if they had more unique names and the database was not static this would cause us to get different results per query. It also makes finding the next/previous pagination set harder since it's non trivial to determine the next start query's look up name if the names were unique and you did not want to parse the names as they are currently. - pagination: Based on the problem set and the aforementioned non-determinism it seems like it would be more effective to return an object with next/previous pagination parameters and possibly some type of trace back for the query itself.
- mongoose queries: I assumed that due to the restriction of not being able to use a library to perform the pagination that we could not use mongoose to perform anything other than finding all of the model's documents and ignore any optimized database query methods we had available. If that were not the case, I would highly prefer to not query the entire set of model documents and instead pass the calculated pagination settings to native mongoose queries like
Model.limit
,Model.find
,Model.skip
, andModel.sort
. The only native call that I used wasModel.lean
in order to speed up the overall query by roughly 4x since we are not using any of the mongoose Model functionality and that did not effect the pagination.
- Clone the repo
- Install the server dependencies with:
npm install
oryarn install
- Install MongoDB to make sure you have the daemon running (or just connect to a MongoDB Cluster you created in the optional Environment step)
- Seed the database by running
yarn seed
ornpm run seed
in the root directory - Run the local backend server using
npm start
oryarn start
- Open
http://localhost:3000
and Have fun!
- Set the following environment variables on | mac | windows | linux | or just create a
.env
file in the root directory - (optional) Create a Mongo DB Cluster to have a persistent hosted database
Environment Variables | Description | Default |
---|---|---|
PORT | Port for the backend express server | 3000 |
MONGODB_URI | URI to log into mongodb | mongodb://localhost/please-set-process-env-mongodb-uri |
MONGODB_LOCAL | override for localhost mongodb database name | undefined |
Returns the json list of apps matching the query params.
Query Parameters | Type | Default | Description |
---|---|---|---|
range.by | String |
"id" |
Only supports querying by "id" or "name". NOTE: Must use |
range.max | Number |
50 |
Maximum number of entries to return. |
range.order | String |
"asc" |
Sets Either 'asc' for ascending order or 'desc' for descending order. |
range.start | String or Integer |
50 or "my-app-001" |
Identifier to match the start entry's range.by property |
range.end | String or Integer |
null |
Identifier to match the end entry's range.by property |
Response | Type |
---|---|
200 |
JSON |
- Run
yarn test
to run the test suite.- Note: You do not have to
yarn seed
in order to test the database but you DO have to have mongodb installed.
- Note: You do not have to
- It will automatically connect to a local mongoose database named mdlive-test if you want to check out the database state after the test suite runs.
- NodeJS - Backend Code
- MongoDB Atlas - Database
- Express.js - Web Framework
- Jest - Testing
- Supertest - API Testing
- CircleCI - Continuous Integration
- CodeClimate - Code Health Metrics