This project is a full-stack personal web portfolio built using Next.js with the App Router, Tailwind CSS, and Sanity.io as a headless content management system (CMS). The goal was to create a responsive, visually engaging, and technically rich portfolio that also demonstrates secure backend integration with advanced authentication logic.
Frontend
Some sections are rendered on the server for performance benefits, while sections with dynamic elements, like the labels cloud, are client rendered.
One of the most unique features is the labels cloud. It randomly positions all labels in 2D space and connects nearby labels, making it a graph. To avoid overlap, I implemented a modified Intersection over Union (IoU) algorithm, an adaptation of a method explored in a prior academic project.
I integrated Sanity.io to manage static content on the site. The experience working with this headless CMS was seamless.
This web app features an admin only login page. Although the admin dashboard wasn’t completed due to time constraints from other commitments, the authentication pipeline itself was fully implemented and designed to be secure. Here follows a high level explanation of authentication: An admin submits credentials via the frontend. The credentials are sent to a Next.js backend route which acts as a proxy. The backend forwards the request to a custom built Java Spring JSON Web Token (JWT) API (more on this later), hosted on Render. If authentication is successful, the JWT API returns a set-cookie header which is forwarded to the client browser. All future requests will now include this cookie for authorization.
Backend
The backend is a stateless Java Spring API that handles authentication using JWTs, hosted on Render. It connects to a PostgreSQL database hosted on Neon. When a request comes in, it is handled by my custom filter. This filter checks what resource is requested and verifies the JWT if available. The JWT holds information about the user, such as user ID and user role. This eliminates the need for database lookups during authorization, which is a huge save in computational power. I paid special attention to error handling and keeping the architecture modular.