Fixing Token Expiry Access in Next.js Layout

Monday, May 19, 2025 – Last week’s issue was related to authentication. Previously, I had already implemented an authentication layout to prevent outsiders or logged-out users from accessing UI pages. Although the backend was already secure, blocking unauthorized data access, we still didn’t want unauthenticated users to even see the UI. That’s why the layout-based authentication was put in place.
However, I noticed a new problem: when a user’s token expired, they could still access the

UI pages. The backend continued to block the data, but the frontend still rendered the components, which wasn’t ideal. So, I decided to add another layer of protection by checking whether the token had expired and redirecting the user to the login page if it had.
On my first attempt, I tried adding the logic directly in the layout file, but I immediately ran into an error “window or localStorage is not defined”. This happens because Next.js layout files run on the server by default, and these browser-specific objects don’t exist in that context.

After looking into it further, I created a custom hook to handle the token expiration logic and tried calling it from the layout. That also didn’t work, since React hooks can only be used inside client components, and layouts are server components by default.
To get around this, I created a new client-only component specifically for token validation. I then placed this component inside the layout, which made the logic work correctly. Thanks to Mr. Peter’s tip on how to simulate an expired token, I was able to test the flow and confirm it behaved as expected.

This also required me to store another value in localStorage, “sessionExpiry” in addition to the existing “isLoggingOut”. With both of these in place, I could now reliably determine whether a user should still have access. The session is considered expired if either there’s no token or if the current time has passed the session expiry timestamp. Now, the UI is finally in sync with the authentication state, users are redirected when they’re no longer supposed to be there.

Improving Error Handling

Monday, May 12, 2025 – Last week, while reviewing the overall project, I came across a minor but important bug related to how error messages were being handled across the application. The issue stemmed from an earlier implementation by a previous intern, where the error handling was too generic. In particular, the logic often defaulted to a fallback else condition, which resulted in inaccurate or misleading error messages being shown to users. This became especially problematic in situations where the error was caused by something specific, like a validation issue or a network failure, but the application simply displayed a vague message like “Unexpected error occured.”

To fix this, I refactored the code and improved the error-handling mechanism to deliver clearer and more accurate feedback. I began by updating the try-catch block that handles API calls. First, I checked if the error was an instance of AxiosError. If the error didn’t contain a response (which typically indicates a network issue), I displayed a message stating “Network error.” This ensures that users understand when the issue is with their internet connection rather than with the application itself.

Next, I refactored a helper function responsible for interpreting and extracting meaningful messages from different error formats returned by the API. This function supports multiple known error structures, such as general error messages, validation errors with multiple fields, and fallback cases where the message is returned as plain text or JSON. This is a significant improvement over the previous implementation, which would have shown a single, generic message regardless of the actual issue.

In summary, this small but important update ensures that users are better informed about what went wrong when an error occurs. It enhances the clarity and reliability of our application’s error reporting and contributes to a better overall user experience.

Preserving Pagination State with URL Syncing

Monday, May 5, 2025 – Last week, I worked on a task where users view a paginated list of data, and when they click into a detail page and return, they should land back on the same page they left. However, the page would always reset to page 1, which was frustrating and not user-friendly. Initially, I was confused because I couldn’t find any logic in the code that explicitly reset the page to 1. Since the project had many layers of logic, Mr. Peter advised me to comment out everything and test step by step to identify the root cause. Following his advice, I eventually discovered that a custom pagination hook was resetting the page to 1 each time. After removing the part of the hook responsible for this, the issue was resolved, the page no longer jumped back to the first one when returning from a detailed view.

Once that was fixed, the next goal was to make pagination even more seamless by syncing the page state with the URL. I created a custom hook that reads the page number from the URL and updates it dynamically as users navigate. This means users can now move between pages, use the browser’s back/forward buttons, and even share links with the exact page state preserved. The hook keeps the UI state and URL in sync, making navigation more intuitive and consistent. This improvement made the pagination feel much more natural and user-friendly, especially when dealing with long lists of data or when users need to return to the same context after viewing details.

Securing Routes and Role-Based Access in Next.js

Monday, April 28 2025 – Last week, I continued working on restricting unauthorized users from directly accessing UI pages, such as when they try to search for a page URL manually. To guide me, I referred to a blog titled “Authorization in Next.js” by Robin Wieruch, published on March 25, 2025. The blog explored several methods for implementing authorization, and I decided to focus on securing routing.

One key takeaway from the blog was the importance of a defense mechanism in place for routing. Without it, unauthorized users might still manage to navigate to the page (which is a security risk). However, if the necessary authorization checks are implemented within the API, Service, and Data Access Layers, unauthorized users won’t be able to read or modify any data. Still, adding this layer of protection not only enhances security but also improves the user experience, which is why it’s highly recommended.

To implement this in my project, I placed all related files into an “authorized” folder and created a layout file that included the necessary security check. The layout file essentially checks whether the user has access; if they don’t, they’re automatically redirected to the login page.

Once that was in place, I focused on implementing role-based authorization, with admin users. I customized the navigation bar to ensure that features specific to certain roles only appear for users with the correct role. To do this, I added a “role” property to the page configuration so that only users with the “Admin” role could see certain features in the navbar.

Finally, I worked on page-level authorization for each individual page. For each one, I added a check to determine the user’s role. If the user is an admin, they can access the page’s data; otherwise, they will be shown an “Unauthorized” page. This added layer of security ensures that only authorized users can access specific content.

Improving UI Usability and Securing Access

Monday, April 21 2025 –Last week, Mr. Peter asked me to redesign part of a page layout to improve readability and make it easier to extract information. The page contained multiple tables and lots of data, so clarity was key. Since the existing layout was developed by the previous person in charge, I first took time to understand the underlying code to ensure a smoother redesign process.

After familiarizing myself with the structure, I experimented with various styles and layout arrangements through trial and error. My goal was to achieve a cleaner, more user-friendly interface and in the end, I landed on a final version that I believe significantly improved the overall look and feel.

In addition to the redesign, I continued testing other features as thoroughly as possible. During this process, I discovered a small but important bug: when creating a new data entry, the recorded time (not the date) was incorrect. Once the underlying issue in the API was fixed, I proceeded to implement the necessary changes, and the bug was successfully resolved.

Toward the end of the week, I began tackling another concern, restricting unauthorized users from accessing the UI page directly. Although they couldn’t access any data, having the UI visible still posed a security concern. After some research, I found an article suggesting several methods to handle this. One approach seemed practical and aligned with our needs, so I discussed it with Mr. Peter. He agreed to let me try it out.

Before making the changes, I cleaned up the code and committed all current updates to avoid mixing them with the upcoming major changes. Although I attempted to implement the new method by the end of the week, it didn’t fully work. I shall continue this task this week.