All articles

First Experience with Next.js Development

May 17, 2024

I recently develeoped a full-stack project using Next.js. Despite encountering many issues, the overall development experience was good. For example, convenient anthentication, Server Actions, and built-in tools.

This project is a photography portfolio showcase website primarily built using Next.js, Prisma, PostgreSQL, ShadcnUI, TailwindCSS, and TypeScript etc.

Project Introduce

Gallery Lantern is a Plog website where I share photographs in my daily lives. The website uses serveral tools: Auth.js for user authentication, next-themes for dark mode switching, and exifr for analyzing photo parameters. Additionally, it integrates geocoding to query addresses and utilizes GPT-4 to generate titles and descriptions for images based on the content. All photographs are stored on Cloudflare R2.

Process

Route Interception

In the previous article, it was mentioned that Auth.js is used for authentication. Centainly, the administration page should not be accessible to everyone, hence the need for interception. Unlike React Router, Next.js allows for the writing of code directly within the middleware.ts.

export default function middleware(
  request: NextRequest,
  response: NextResponse
) {
  const pathname = request.nextUrl.pathname;
  if (pathname === PATH_ADMIN) {
    return NextResponse.redirect(
      new URL(PATH_ADMIN_PHOTOS, request.url)
    );
  }
  return auth(
    request as unknown as NextApiRequest,
    response as unknown as NextApiResponse
  );
}

In addition to, it is necessary to supplement the callbacks option in NextAuth(creating an Authentication method) for the purpose of determining route permissions.

export const {
  handlers: { GET, POST },
  signIn,
  signOut,
  auth,
} = NextAuth({
  //...
  callbacks: {
    authorized({ auth, request }) {
      const { pathname } = request.nextUrl;

      const isUrlProtected =
        isPathProtected(pathname);
      const isUserLogin = !!auth?.user;

      const isAuthorized =
        !isUrlProtected || isUserLogin;

      return isAuthorized;
    },
  },
  // ...
});

At now, without logging in, access to management page is impossible.

Prisma Client

Here, I will not dive into the introduce and installion of PostgreSQL. After installing the prisma dependency, executing npx prisma init into initialize the schema.prisma file, which is where data models are defined. Additionally, adding environment variables(such as database link address) to the .env.local file.

Currently, installing prisma client for accessing the database.

npm install @prisma/client
// schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Photo {
  id                    Int       @id @default(autoincrement())
  title                 String    @db.VarChar(255)
  url                   String    @db.VarChar(255)
}

  • If schema is modified, run npx prisma migrate dev to synchronize the changes with the database. The migrate command will automatically update the Prisma Client, so it is necessary to run prisma generate again.

Finally, avoiding instantiating extra PrismaClient instances. The solution is in this case to instantiate a single instance PrismaClient and save it on the globalThis object.

import { PrismaClient } from "@prisma/client";

let prisma: PrismaClient;

if (process.env.NODE_ENV === "production") {
  prisma = new PrismaClient();
} else {
  let globalWithPrisma =
    global as typeof globalThis & {
      prisma: PrismaClient;
    };
  if (!globalWithPrisma.prisma) {
    globalWithPrisma.prisma = new PrismaClient();
  }
  prisma = globalWithPrisma.prisma;
}

export default prisma;

antcao.me © 2022-PRESENT

: 0x9aB9C...7ee7d