Skip to content

civilcoder55/note-taking-app

Repository files navigation

Note Taking API

Usage with docker

  1. Clone the repo

    git clone git@github.com:civilcoder55/note-taking-app.git

    or

    git clone https://github.com/civilcoder55/note-taking-app.git
  2. Change directory

    cd note-taking-app
  3. update the env file with proper Leave it if you want, default values should work

    cp .env.example .env
  4. ensure run.sh is executable

    chmod +x run.sh
  5. Build images and run containers

    docker-compose up -d
  6. Create and Sync database (already run with container up)

    docker exec -it note-taking-node npm run db:sync
  7. Access API at

    http://localhost:3000/v1/docs
  8. Access docker logs

    docker logs -f note-taking-node
  9. To run test

    docker exec -it note-taking-node npm run test

Usage without docker

  1. Clone the repo

    git clone git@github.com:civilcoder55/note-taking-app.git

    or

    git clone https://github.com/civilcoder55/note-taking-app.git
  2. Make sure you have MySQL database and Redis server installed

  3. Change directory

    cd note-taking-app
  4. Update the env file with proper variables

    cp .env.example .env
  5. Install npm dependencies

    npm install
  6. create and Sync database

    npm run db:sync
  7. To run tests

    npm run test
  8. To run app

    npm start
  9. Access API at

    http://localhost:3000/v1/docs

API Documentations and Collections

  • You can access swagger API docs at http://localhost:3000/v1/docs
  • or you can use the Postman collection which includes all the different response examples
  • Click on the image to download the collection

screens

Break down the task requirements

ORM :

  • used Sequlize orm with MySQL database.
  • defined two models for Note and User.
  • used sync to migrate and update the status of database tables and schema, I could used migration files as best practice to keep the state of the database changes but for simplicity I went for the sync command
  • i could use models associations and query notes on user instance but i keep it simple by query notes with user_id

database schema :

  • users table with id as primary key and a unique index on emails
  • notes table with id primary key and user_id as foreign key and unique composite index on (user_id, title)

ERD

Indexes


for API endpoints :

  • implemented the following
Auth Endpoints
Method URL Description
GET /v1/auth/me Retrieve logged in user data.
POST /v1/auth/login Login with email and password.
POST /v1/auth/register Register as new user.
Notes Endpoints
Method URL Description
GET /v1/notes Retrieve all user notes.
GET /v1/notes/:id Retrieve user note by id.
POST /v1/notes Create a new note.
PATCH /v1/notes/:id Update a note.
DELETE /v1/notes/:id Delete a note.

Caching

  • used Redis to cache most accessed notes

  • used read through strategy

    In a real-world scenario, I will handle cache in more smart ways, like adding expiring time or setting max memory for say 100 mb and set evection policy allkeys_lru to delete lease recently used notes


Authentication

  • used JWT simple tokens with an expiration time, to handle authentication in a stateless manner

    In a real-world scenario, I will go for short_life access tokens and long_life refresh tokens, for example, using passport lib, it's more secure and standarized

  • For logout, I didn't implement it as it is not part of the assessment scope

    In real world scenario I will handle it by invoking the token either by keeping its state in the database and checking for existence on validate or remove it on logout If I need to keep it stateless and don't keep track of tokens, all I need to do is store only blacklisted tokens, When validating check if not blacklisted and when logout add a token to the blacklist store (redis for example)


Validation

  • Used Joi lib to define validation schemas
  • Defined a validator middleware to validate schemas against request body, params, and queries

Dockerizing the application

  • I create a Dockerfile for the application's main image, it is a node image with installed npm dependencies and application data
  • used Redis' latest official image
  • used MySQL's latest official image
  • Create a docker-compose file for the stack to run 1 container of each image, two named volumes for datastores data, one bridge network for all stack
  • created bash script to wait for mysql server ready then sync database and run nodejs server

Unit tests

  • I used Jest to write unit and integration tests

Tests

  • you can see coverage report at coverage/index.html after running

      npm run coverage

    or

     docker exec -it note-taking-node npm run coverage

CI/CD

  • I used GitHub actions to write a simple ci pipeline to ensure code linting and pass tests
  • I didn't write a cd pipeline, I couldn't find the proper environment or server to deploy (free Heroku Dynos are gone 😭)

---

Design patterns

  • singleton pattern

    • Node modules by default implement a singleton pattern, it caches the module on the first import/require then uses the same instance on every upcoming import/require https://nodejs.org/api/modules.html#caching

    • So we don't actually need to apply this pattern for say logger but let's rebuild it to implement the actual pattern I rebuilt it (but not used) in src/config/singletonLogger.js and covered both singleton and commonJs modules with two test cases

  • Factory method pattern https://github.com/civilcoder55/note-taking-app/commit/f7e7a833fb0510d6077392a63850ab863a61bc7d

    • I didn't find a need to do the factory method but I made some edits to make a proof of concept of the pattern
    • First, i added two new columns to the notes table to distinguish between work and personal notes
    • then added data object classes for each work and personal that both inherit from note to extend shared behavior
    • then added note factory to return the correct dto based on the type
    • then consumed this factory in the note create and update service to return correct data based on note type before persisting it on the database

Built With

  • Node (v20.5)
  • Express (4.17)
  • MySQL (8.0)
  • Redis (7.0)

ToDo

  • Read and analyze task doc
  • boilerplate and init new express app and setup initial configs
  • dockerize application stack
  • setup mysql database
  • create database models and sync them
  • add logger
  • create users register/login API endpoints
  • Create user notes API endpoints
  • add caching for notes endpoint
  • Add swagger and postman documentation
  • Write unit and integration tests using jest
  • add a singleton design pattern
  • add factory design pattern
  • optimize and refactor the code
  • create CI pipeline using GitHub actions
  • finalize readme file