Musicn Next: The rebuild

Wait, why is there another Musicn?

When I first created the other Musicn, I was putting my skills at that time to use. I used my knowledge in API design, React, and the PERN stack to create the application.

As time passes, new technologies emerge, and I want to use them to create the best possible application.

Introducing, The new Musicn

Musicn-next (or the new Musicn) is powered by Next.js and Serverless architecture. The speed is unbelievable as compared to the previous Musicn.

Super-fast Musicn?

The problem with the old Musicn is that it is a slow website. It is really slow. Each API request can take up to 2 seconds, which is such a big no-no, especially for users.

In research by Jakob Nielsen titled "Respone Times: The 3 Important Limits", he said:

  • 0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.
  • 1.0 seconds is about the limit for the user's flow of thought to stay uninterrupted, even though the user will notice the delay. Normally, no special feedback is necessary during delays of more than 0.1 but less than 1.0 seconds, but the user does lose the feeling of operating directly on the data.
  • 10 seconds is the limit for keeping the user's attention focused on the dialogue. For longer delays, users will want to perform other tasks while waiting for the computer to finish, so they should be given feedback indicating when the computer expects to be done. Feedback during the delay is especially important if the response time is likely highly variable since users will then not know what to expect.

It is important to note that the response time is not the same as the time it takes to render the page and the time it takes to send the response to the user. And it is my mission to make the response time as fast as possible.

The Initial Problem

Converting all current existing APIs

The old Musicn has many endpoints. And a bulk of them is focused on authentication, and my goal with the new Musicn is to make it a full-stack application. This new goal came to light so that development will be maintained on ONE codebase.

Solution

However, Next.js allows us to make API routes simply by making a file in the pages/api directory. The mapping of the route will be the filename. So user.ts will map to /api/user in the URL. How cool! Moreover, Next.js API's API and syntax are akin to Express.js, making the switch even easier!

Better solutions to query data from the database

The old Musicn uses pg for ultimate speed. But there is a trade-off regarding readability and how developers can get on board with development. It is hard because you need to type in SQL databases manually, and then you need to know the table names, column names, the datatype, and relations. AH! IT'S A MESS!

Angry

To integrate an ORM into the old Musicn will be counter-productive and many of its codes need to change, and that will make everyone go insane.

Solution

This new Musicn has one goal, and that is to use an ORM to get data. And Prisma is the ORM of choice. it is our choice because of how developer-friendly it is, the autogeneration of the schema files, and many other features that make it stand out from other ORMs such as Sequelize and TypeORM.

The shortcomings

No implementation has 0 shortcomings, it's always a compromise. And building the new Musicn, I am humbled by some of the shortcomings.

Next JS middleware

Although Next.js has a similar syntax to Express.js. One of the things I missed in Express.js is the middleware. After one hour of looking for middleware solutions and then looking for more, even trying the new Middleware feature introduced by Vercel. I used an old method:

import APITokenHandler from '@/util/APITokenHandler';
import { NextApiRequest, NextApiResponse } from 'next';

export default function withProtect(
    handler: IHandler,
    rejectWhen: APITokenHandler.REJECT_WHEN = 'none'
) {
    return (req: NextApiRequest, res: NextApiResponse) => {
        console.log(
            `=====CHECKING FOR TOKEN IN ${req.url}, (REJECT TYPE = ${rejectWhen})=====`
        );

        // TODO: Cleanup code
        if (rejectWhen === 'none') {
            if (!APITokenHandler.hasAPIToken(req)) {
                return APITokenHandler.reject(rejectWhen, req, res);
            }
        } else if (rejectWhen === 'has') {
            if (APITokenHandler.hasAPIToken(req)) {
                return APITokenHandler.reject(rejectWhen, req, res);
            }
        }

        return handler(req, res);
    };
}

And then, it is used by wrapping the API handler:

export default withProtect(handler as IHandler);

Deployment

Vercel provides hosting for Next.js sites, and it's a one-click solution. Register for an account, link your GitHub, select your repo and deploy! (and also some configurations of environment variables)

Some tweaks I have done are:

Changing the country of where the serverless function runs

By changing the Serverless Function Region, the speed of API calls increased significantly. (and obviously will introduce some extra time if someone is further away from the Singapore server) image.png

Using Caching headers

Read more: nextjs.org/docs/going-to-production#caching

Using caching, I can cache responses for the APIs and pages. This makes it fast! I even made a class to handle caching headers for me:

export default class Cache {
    private static headerName: string = 'Cache-Control';

    /**
     * Revalidates the cache in the edge server while showing the old stale data meanwhile.
     * @param res NextApiResponse
     */
    public static revalidateInBackground(res: NextApiResponse): void {
        res.setHeader(this.headerName, 's-maxage=1, stale-while-revalidate');
    }

    /**
     * Caches the data in the edge server and expires after the given seconds, to which, it'll refetch and cache new data again.
     * @param res NextApiResponse
     * @param expiresAfterSeconds number
     */
    public static inEdgeServer(
        res: NextApiResponse,
        expiresAfterSeconds: number
    ): void {
        res.setHeader(this.headerName, `s-maxage=${expiresAfterSeconds}`);
    }
}

Conclusion

I have learned a lot about the React ecosystem and the ecosystem of Musicn. I have redefined endpoints to be more useful and as simple as possible.

Along the development journey, I learned more about technologies and how they work, and how it is important.

I learned even more stuff from when I built the old Musicn.

  • Caching
  • Server-side rendering
  • Next.js
  • Prisma (in production)
  • Serverless architecture

Rebuilding applications using new shiny technology is always fun, it allows you to explore and learn more (even refresh your memory on what you thought you knew).

My message to upcoming aspiring developers: Build what you're passionate about, and continue building it. There's never a "complete" software, there's always room for improvement, and you shall pursue it in terms of watching talks of technologies and figuring out a way how to integrate it with your product.

Did you find this article valuable?

Support Nabil's Developer Adventures by becoming a sponsor. Any amount is appreciated!