Skip to content

hussein-m-kandil/odin-blog-author

Repository files navigation

A screenshot of the app's home page showing statistics and blog posts with their images, titles, contents, tags, and more.

A blog authoring 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:

  1. Backend API, which I built as part of the generic-express-service.
  2. Blog viewer: which I built in odin-blog-viewer.
  3. Blog author: which I built in this project for authoring/viewing the blog posts.

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.

Important Notes:

  1. Multiple Authors: I have extended the author website (this project) to accept multiple authors, so any signed-up account can author a blog post.
  2. Guest Sing-in: I have added the ability to sign in as a guest to facilitate trying the app.
  3. Backend State Reset: I built these projects to showcase what I am learning in web development, and I don't plan to keep maintaining them; therefore, I added a reset feature within the generic-express-service to periodically delete any non-admin data.

Tech Stack


Challenges

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.

  • Reusable Dialog with Form Component: I created a dialog system via context, so I can avoid repeating it on every usage (DRY). However, when placing a reusable form inside a dialog, I needed the dialog to stay open while the form had unsaved changes. To solve this, the form exposes a shouldUnmount function as ref within an imperative handle, and the form's parent passes it to the dialog, which expects it and calls it before closing. Check out the dialog context and, for example, the PostForm for implementation details.

  • Image 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 out ImageToolkit, MutableImage, ImageInput, and ImageForm for 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.


Features

  • 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.
    • Middleware validation in Next.js.
  • Blog Management

    • Create, update, delete, and search posts.
    • Infinite scroll powered by TanStack Query.
    • Attach images to posts.
  • Comments

    • Add, update, and delete comments.
    • Infinite loading of comments.
  • User Profiles

    • View/Edit profile info (username, bio, avatar).
    • Profile-specific post listings.
    • delete account.
  • Images

    • Upload and store images using the backend service.
    • Simple in-app image editor to adjust position.
    • Temporarily delete/edit an image until submission.
    • Automatic image browser-cache invalidation, for real-time mutations.
  • Reusable Components

    • Dialog system via context (global dialogs for forms, confirmations, etc.).
    • Dynamic Form system with react-hook-form, reusable across the app.
    • Image toolkit for image editing (positioning, deleting).
    • Shared UI primitives from shadcn/ui.
  • 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.

Local Development

Requirements

Setup

  1. Clone and set up the backend

    Refer to generic-express-service for 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.

  2. Clone, install, and configure the Next.js app

    git clone https://github.com/hussein-m-kandil/odin-blog-author.git
    cd odin-blog-author
    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.

  3. 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 https scheme is mandatory. So, connecting to the local http server 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-https option with the Next.js dev server.

Releases

No releases published

Packages

 
 
 

Contributors

Languages