A blog viewing app built as part of the required projects at The Odin Project Node.js course.
The course requirements for this project are divided into 3 pieces:
- Backend API, which I built as part of the
generic-express-service. - Blog Author Site: which I built in
odin-blog-author. - Blog Viewer Site: which I built in this project for only viewing the blog posts and interacting with them.
Here are the requirements from the course website:
We’re actually going to create the backend and two different front-ends for accessing and editing your blog posts. One of the front-end sites will be for people that want to read and comment on your posts, while the other one will be just for you to write, edit and publish your posts. Why are we setting it up like this? Because we can! The important exercise here is setting up the API and then accessing it from the outside.
- TypeScript
- Zod for input validation
- React with its Context API
- React Hook Form for controlled form state
- React Testing Library for component testing
- Next.js with SSR and
fetchfor server HTTP requests - TanStack Query for data fetching and caching
- Motion (prev Framer Motion) for animations
- axios for browser HTTP requests
- date-fns for date formatting
- shadcn/ui for UI primitives
- Tailwind CSS for styling
- vitest for running tests
While building this project, I faced several challenges, such as:
-
Authentication with SSR: I wanted the backend to handle auth while still leveraging Next.js SSR. The solution was to introduce a custom Next.js API route that proxies auth requests, maintains a cookie between the Next server and frontend, and uses a Bearer token schema between the Next server and backend. Check out the auth route, auth module, and auth context for implementation details.
-
Avatar Uploader & Editor: The backend image upload was already a challenge in the
generic-express-service. On the frontend, I needed to show the selected image immediately and allow deleting the image or positioning it within the image boundaries, while not committing any of these changes until submitting the image form. I built a small custom image toolkit to handle this. Check outImageToolkit,MutableImage,ImageInput,ImageForm, andUserProfilefor implementation details. -
Reusable/Customizable Tested Components: I tried hard to build this app consists of multiple reusable/customizable components to be useful for future usage, and to facilitate the app's maintainability.
-
Authentication & Authorization
- Custom solution that bridges SSR in Next.js with backend auth.
- Auth API route in Next.js acts as a middle layer between frontend and backend.
- Auth context on the frontend with cookie-based session between the Next.js server and its frontend.
- Bearer token schema between Next.js and the backend.
-
Comments
- Add, update, and delete comments.
- Infinite loading of comments.
-
User Profile
- View/Edit profile info (username, bio, avatar).
- Profile-specific post listings.
- delete account.
-
User Avatar
- Upload and store avatar using the backend service.
- Simple in-app avatar editor to adjust position.
- Temporarily delete/edit an avatar until submission.
- Automatic image browser-cache invalidation, for real-time mutations.
-
UI/UX
- Animated components via Motion (prev Framer Motion).
- Loading skeletons for better perceived performance.
- Responsive, dark/light/system mode.
- Toast notifications for global feedback.
-
Testing
- Component tests with
vitest.
- Component tests with
- Node.js (20 LTS or later)
- Clone of
generic-express-service
-
Clone and set up the backend
Refer to
generic-express-servicefor more details.git clone https://github.com/hussein-m-kandil/generic-express-service.git cd generic-express-service npm install # Refer to `.env.test` to configure .env (DB connection, ports, etc.) # Start the PostgreSQL database npm run pg:up # Build the backend source code npm run build # Start the backend production server npm run start
Make note of the backend base URL, which should be
http://localhost:8080/api/v1. You will need it in the frontend.env. -
Clone, install, and configure the Next.js app
git clone https://github.com/hussein-m-kandil/odin-blog-viewer.git cd odin-blog-viewer npm install cp env.sample .env cp env.sample .env.test npm run test -- --run npm run dev
The app will be available at:
http://localhost:3000. -
Useful scripts
- Start dev server:
npm run dev - Build:
npm run build - Run tests:
npm test - Lint:
npm run lint - Type check:
npm run type-check - Start production server:
npm run start
Important Note: A secure cookie is used for authentication between the browser and the Next.js server, hence an
httpsscheme is mandatory. So, connecting to the localhttpserver via local IP address from another device (e.g, mobile phone), won't work as expected, and you will never leave the signin/signup pages even after a successful sign-in. The only solution that I know for this situation is using the--experimental-httpsoption with the Next.js dev server. - Start dev server:
