You are asked to implement a small single-page web application in JavaScript (you may use any modern framework or plain JavaScript) to display and interact with a list of blog posts.
Implement the features in four incremental levels.
Data Model
Initially, you are given a local JSON file posts.json with an array of blog posts:
[
{
"id": 1,
"title": "First post",
"body": "Hello world",
"authorId": 101,
"likes": 3
},
{
"id": 2,
"title": "Second post",
"body": "Another post",
"authorId": 102,
"likes": 5
}
]
For the API-based levels, assume the backend exposes these HTTP endpoints:
-
GET /api/posts
-
Returns an array of posts in the same shape as above (with
authorId
instead of full author info).
-
GET /api/authors/:id
-
Returns a single author object:
{
"id": 101,
"name": "Alice"
}
-
GET /api/me
-
Returns the current logged-in author:
{
"id": 101
}
Assume all endpoints return JSON. Network requests can fail.
Level 1: Render posts from local JSON
-
Load the posts from the local
posts.json
file.
-
Render a list of blog posts to the page.
-
For each post, display at least the title and body.
-
There is no need to show author information or likes at this level.
Level 2: Add a form to create posts
Extend your page with the ability to add a new blog post on the client side.
-
Add a form with the following inputs:
-
title
(text)
-
body
(multi-line text)
-
When the user submits the form:
-
Create a new post object with a unique
id
(you may generate it on the client), an arbitrary
authorId
for now, and
likes = 0
.
-
Append it to the existing list of posts without reloading the page.
-
Clear the form after successful submission.
You are not required to persist the new post to a backend; it is sufficient to manage it in client-side state.
Level 3: Replace local JSON with API calls and show authors
Now replace the local JSON source with remote API calls.
-
On page load, fetch all posts from
GET /api/posts
instead of using
posts.json
.
-
For each post, fetch its author information from
GET /api/authors/:id
using
authorId
from the post.
-
Fetch these author details in parallel/asynchronously (do not block on one author before starting the next if possible).
-
Fetch the current user from
GET /api/me
.
-
In the UI, for each post:
-
Display the post's title and body (as before).
-
Display the author label:
-
If the post's
authorId
is equal to the current user's
id
(from
/api/me
), display
You
instead of the author's name.
-
Otherwise, display the author's
name
returned by
/api/authors/:id
.
-
Implement basic loading and error handling:
-
Show a loading state while posts are being fetched.
-
If fetching posts fails, show an error message instead of the list.
-
If fetching an individual author fails, still show the post but display an author label such as
Unknown author
.
You may assume that GET /api/posts and GET /api/authors/:id can be called concurrently and may resolve in any order.
Level 4: Add likes and keep posts sorted by likes
Add an interactive "Like" feature to each post.
-
For each rendered post, display:
-
The current number of likes (from its
likes
field).
-
A
Like
button.
-
When the user clicks the
Like
button on a post:
-
Increment that post's
likes
count in the UI immediately (no page reload).
-
Re-sort the list of posts on the client so that posts are always displayed in
descending order of likes
(most liked first).
-
The sorting should update dynamically after each click. For example, if you repeatedly click the like button on a post that starts below another, it should move above the other post once its
likes
count becomes higher.
You do not need to persist likes to the backend for this exercise; manage them in client-side state. Focus on correct UI behavior, state management, concurrency of API calls, and error handling.