Skip to content

Interview Exercise - Node + Express + Sequelize

Notifications You must be signed in to change notification settings

rootux/interview-wis

Repository files navigation

Wisdo exercise

The task was as follows: You are required to implement a node.js REST webserver which supports a basic content system.

You are not required to write the frontend part for this system, only the backend. The exercise should be written using typescript/javascript (typescript preferred). You can use whatever web framework you wish (Express, Koa…) and select the Database you feel best suites this system (mongodb, postgres…).

You can design the DB schemas and structure the app’s business logic as you see fit, in addition to exposing as many APIs as you feel to support the system features (will follow). The aim of this exercise is for me to get to know your coding style. I’m looking for a clean and well structured code.

Business entities

Post

  • Title – text with up to 60 chars Summary – text with up to 150 words
  • Body – text
  • Author – the author of this post (the user which uploaded this post)
  • Community – the community this post belongs to (See community)
  • Likes – a number representing the number of likes this post got
  • Status – Pending approval/Approved. A post is in pending state until it’s approved by a moderator/super moderator. Once it’s approved it’s becomes publicly open (see Viewing + approving posts)

For the purpose of this exercise assume that the system needs to support tens of thousands of posts in the foreseeable future.

Community Each user belongs to any number of communities. In addition each post belongs to a certain community

Each community should have the following properties:

  • Title – text with up to 60 chars
  • Image – url. For the purpose of this exercise you don’t have to support image uploading and can assume it was already uploaded using a different system
  • Member count – Number of users who joined this community For the purpose of this exercise assume that the system needs to support hundreds of communities in the foreseeable future.

User

A user in the system. Each user should have following properties:

  • Name – text
  • Role – optional property for users with a role in the system. The system needs to support 2 roles - super moderator and moderator (see Features to implement). each user can only have 1 role.
  • Email - optional property for users with emails. Most moderators + super moderators have it but not all of them.
  • Image - url. This property is optional. For the purpose of this exercise you Don’t have to support image/video uploading and can assume it was already uploaded using a different system
  • County – the country the user is from. You can assume this is always defined.
  • Communities – a user can belong to any number of communities.

For the purpose of this exercise assume that the system needs to support hundreds of thousands of users in the foreseeable future.

Watchlist word

A list of “problematic” words that should trigger an alert when a user writes 1+ of them in a certain post(see Features to implement)

For the purpose of this exercise assume that the system needs to support hundreds of watchlist words in the foreseeable future.

Features to implement

Authentication

You don’t have to support any form of actual authentication. Please create a dummy middleware/function that allows fetching a dummy userId for authorization

Uploading new posts

A user should be able upload a post assigned to one of his communities. He should not be allowed to upload posts to other communities.

“summary” is not mandatory. If it’s not present, it should be generated by the system by selecting the first 100 words from the post’s “body”.

Content watchlist detected alert

If an uploaded post contains one or more of the watch list words it should “send” an email alert for all the moderators + super moderators.

For the purpose of this exercise you don’t actually have to implement sending emails. instead use the following dummy function:

function sendEmail({ to, subject, body }: { to: string[], subject: string, body: string }) { console.log("sendEmail called", {to, subject, body}) }

The email text is not important but it should include a link to the API for fetching the uploaded post (in a real world app this link would be to the page presenting this post which is not needed for the exercise)

This email alert should not be coupled to adding the actual post – failing to send the alert should not fail adding the post.

Approving posts

Moderators + super moderators should be able to approve posts. Once they do they become publicly available.

Feed

A section In the app where the user sees posts which are “recommended” to him. Ranked by “relevance” score - descending

The Feed should only include posts which belong to one of the requesting user’s communities

The algorithm for this feature should rank posts where the post author is from the same country first, then based on the following weighted score - 80% reaction count + 20% post length. this weighted score doesn’t have to be 100% accurate and can be calculated every X hours (if you wish to use some sort of offline scoring mechanism)

Some examples:

Post A author is from the same county as the requesting user, post B isn’t. A is ranked higher then B (returned first in the array) even if B has a higher weighted score

Post A and B authors are from the same county as the requesting user. The post with the highest weighted score is returned first

Post A and B authors are not from the same county as the requesting user. The post with the highest weighted score is returned first

No posts are found from one of the users communities - the feed is empty (empty array response)

What to submit:

A link to a public repository on GitHub which includes a Dockerfile and docker-compose files (to run server+db) in addition to a README.md file with instructions/comments you wish to include.

The project should also include npm scripts for stating the web server (in dev mode) and running its tests (optional, but highly recommended of course 😊).

--End of task requests--

First time run

npm run docker:startd
# Then, Seed the database with data
npm run docker:seed
# Then to see logs you can run
npm run docker:start

Then the server would be up on http://localhost:3000 (You can use the POSTMAN file at assets/ folder to test the server manually or jump to the Testing section)

Alternative local debug

# Install NPM
# (FOR MAC)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
# OR (FOR WINDOWS) - navigate to https://github.com/coreybutler/nvm-windows/releases
nvm use
npm i -g nodemon
npm i
docker-compose up -d db
npm run start:dev

Testing

Run npm run test

Structure

More info

Dockerfile - uses multi-stage build - has dev and production envs

DB Schema

Db Schema

Workers

Assume npm run update-feed would run every 3 hours and would repopulate RankByReaction and RankByLength tables

Comments

  1. This is my first production like Typescript project. I may error on the side of types and where to put declarations - I wanted to try typescript as recommended but this may not be the most well-structured typescript
  2. Some race condition might occur. If a user scrolls the system, while the background worker is active (set to run every 3 hours window) - trying to fetch next from feed would result inaccurate data.
  3. For production - I'll probably use redis for caching instead of node-cache (CacheService) - in our case since the word list is < 100 words - memory only seem valid here
  4. Given more time - Part of the integration tests can be moved into a unit test by injecting them some kind of repository instead of the actual database
  5. I've used post reactions with reaction type to support different reaction in the future (Love, Cry,...) - those should be in sync with the likes count on the post model
  6. Given more time - I would unittest the express routes for security measurements
  7. Given more time - I would fix country to be an enum (encountered this issue
  8. Given more time - I would add newman e2e testing to validate all is working as expected
  9. Given more time - I would increase coverage from 67% to 90%
  10. Given more time - I would add linter + husky (To validate commits aligned with linter)

Alternative Designs

  1. An alternative to sequelize would be prisma which is a raising star - I don't have enough experience with it - given more time - I might consider it
  2. For security purposes - I might set some model ids to UUID so they can't be guessed in a bruteforce way
  3. Nestjs is an uprising star that has built in Dependency Injection, Opinionated framework https://nestjs.com/

Releases

No releases published

Packages

No packages published