<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: wangfengyuan</title>
    <description>The latest articles on DEV Community by wangfengyuan (@wangfengyuan).</description>
    <link>https://dev.to/wangfengyuan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1097274%2Fe36888f6-855b-40fe-9e3f-fcfce6da0ac7.jpeg</url>
      <title>DEV Community: wangfengyuan</title>
      <link>https://dev.to/wangfengyuan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wangfengyuan"/>
    <language>en</language>
    <item>
      <title>Rapid development of full-stack navigation sites using Nextjs</title>
      <dc:creator>wangfengyuan</dc:creator>
      <pubDate>Thu, 08 Jun 2023 02:00:23 +0000</pubDate>
      <link>https://dev.to/wangfengyuan/rapid-development-of-full-stack-navigation-sites-using-nextjs-5clm</link>
      <guid>https://dev.to/wangfengyuan/rapid-development-of-full-stack-navigation-sites-using-nextjs-5clm</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;With the hotness of &lt;code&gt;ChatGPT&lt;/code&gt;, many developers responded quickly with a spurt of AI applications applied to different scenarios, and basically focused on &lt;code&gt;web&lt;/code&gt; domain applications, and behind the rapid development, we can see that developers mostly choose &lt;code&gt;Next.js&lt;/code&gt; or &lt;code&gt;Nuxt.js&lt;/code&gt; full-stack frameworks to develop in order to quickly validate their products. The main reasons behind this selection, I think, are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The importance of &lt;code&gt;SEO&lt;/code&gt; &lt;br&gt;
Foreign countries pay more attention to the importance of SEO, domestic search engines mostly rely on money to buy search traffic, including small programs, App such as the demand for SEO is not much&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The rise of &lt;code&gt;Edge Function&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Serverless enables front-end development to quickly develop full-stack applications and conveniently host their own back-end services without paying too much attention to deployment, however, his disadvantage is that most Serverless are using containerized solutions, so the cold start time is long, if the request is forwarded in their own cloud function OpenAI interface, it may happen that the request time is very long. Nowadays, vendors such as Vercel, CloudFlare, Supabase, etc. have Edge Function capability, which allows the function to run in some edge nodes closer to the user. For faster cold start time to respond to users quickly, this solution generally also adds some restrictions, but still favored by many developers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Diversity of Cloud Service Vendors&lt;/p&gt;

&lt;p&gt;Cloud service vendors provide many basic services, and when hosting projects with services such as Vercel, they can integrate with Github for continuous deployment and also assign a domain name. Many other vendors also offer a number of free back-end storage services, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis provided by &lt;a href="https://upstash.com/"&gt;Upsatsh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL from &lt;a href="https://supabase.com/"&gt;Supabase&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MySQL provided by &lt;a href="https://planetscale.com/"&gt;PlantScale&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;User authentication and user management from &lt;a href="https://clerk.com/"&gt;Clerk&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These vendors' free plans are perfectly adequate for personal development, but you can of course use paid plans depending on the size of the product&lt;/p&gt;

&lt;p&gt;And the purpose of this article is to try to develop a simple navigation page to satisfy my own collection fetish for freeing my favorites, to learn &lt;code&gt;Next.js&lt;/code&gt; development, and to experience the ease of developing full-stack applications brought by &lt;code&gt;Next.js&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the project
&lt;/h2&gt;

&lt;p&gt;With the emergence of atomized &lt;code&gt;CSS&lt;/code&gt; solutions like &lt;code&gt;tailwindcss&lt;/code&gt; and &lt;code&gt;unocss&lt;/code&gt;, there are many UI component libraries derived from them, such as &lt;a href="https://www.radix-ui.com/"&gt;Radix&lt;/a&gt;, &lt;a href="https://daisyui.com/"&gt;daisyUI&lt;/a&gt;, &lt;a href="https://flowbite.com/docs/getting-started/introduction/"&gt; flowbite&lt;/a&gt;, of which the RadixUI component library pays much attention to web accessibility, and the components follow the &lt;a href="https://developer.%20mozilla.org/zh-CN/docs/Learn/Accessibility"&gt;WAI-ARIA standard&lt;/a&gt;, which makes it easier for developers to build accessible UI interfaces, and because it focuses on accessibility and belongs to &lt;code&gt;Headless UI&lt;/code&gt; which means no specific style class name code, &lt;a href="//https://%20twitter.com/shadcn"&gt;shadcn&lt;/a&gt;The author of &lt;a href="https://ui.shadcn.com/"&gt;shadcn/ui&lt;/a&gt; developed a component library, which is based on the &lt;code&gt;RadixUI&lt;/code&gt; component library and gives a simple and beautiful style, which is favored by many developers.&lt;/p&gt;

&lt;p&gt;Here is a direct clone of the author's &lt;a href="https://github.com/shadcn/next-template"&gt;Nextjs template&lt;/a&gt; initialization project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/shadcn/next-template&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project uses the latest &lt;code&gt;app router&lt;/code&gt; version of &lt;code&gt;Next.js&lt;/code&gt; and has integrated &lt;code&gt;tailwindcss&lt;/code&gt; and &lt;code&gt;shadcn/ui&lt;/code&gt; component libraries. Here we choose to do the navigation site also because it is simple enough, the key style is for the sidebar, because &lt;code&gt;tailwindcss&lt;/code&gt; is mobile first, so here set the default hidden, when the screen width is larger than &lt;code&gt;sm&lt;/code&gt; show.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"fixed z-20 hidden min-h-screen sm:block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    ...
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for the list area, use &lt;code&gt;grid&lt;/code&gt; layout, default mobile priority one column, depending on the screen size to display &lt;code&gt;2&lt;/code&gt; or &lt;code&gt;3&lt;/code&gt; columns&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-1 gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   ...
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other styles can be borrowed from other people's website design simply beautify the next, for not familiar with &lt;code&gt;css&lt;/code&gt; and lack of aesthetic I spent a lot of time in adjusting, always feel that the site is not beautiful enough, but do not know how to beautify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Define the model
&lt;/h3&gt;

&lt;p&gt;Database integration here I chose &lt;code&gt;Prisma&lt;/code&gt;, similar to the more popular and &lt;a href="https://orm.drizzle.team/"&gt;&lt;code&gt;Drizzle&lt;/code&gt;&lt;/a&gt;, for &lt;code&gt;Prisma&lt;/code&gt; specific concepts, use and its advantages, you can refer to my organized record of &lt;a href="https://blog.codefe.top/%E9%9D%A2%E5%90%91nodejs%E5%92%8Ctypescript%E7%9A%84%E4%B8%8B%E4%B8%80%E4%BB%A3-orm%E5%B7%A5%E5%85%B7prisma"&gt;notes&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;npx prisma init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will create the &lt;code&gt;prisma/schema.prisma&lt;/code&gt; file, creating the model as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;generator client {
  provider = "prisma-client-js"
}

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

model Category {
  id String @id @default(cuid())
  icon String
  title String
  description String
  rank Int?
  createdAt DateTime @default(now()) @map(name: "created_at")
  updatedAt DateTime @default(now()) @map(name: "updated_at")
  links Link[]

  @@map(name: "category")
}

model Link {
  id          String   @id @default(cuid())
  icon        String
  url         String
  title       String
  description String
  rank        Int?
  public      Boolean  @default(true)
  status      Int      @default(1) @db.TinyInt
  createdAt   DateTime @default(now()) @map(name: "created_at")
  updatedAt   DateTime @default(now()) @map(name: "updated_at")
  cid         String
  catagory    Category @relation(fields: [cid], references: [id])

  @@map(name: "link")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;DATABASE_URL&lt;/code&gt; is the remote database address, here I used &lt;code&gt;MySQL&lt;/code&gt; from &lt;code&gt;PlantScale&lt;/code&gt;. Of course you can also use &lt;code&gt;PostgreSQL&lt;/code&gt; from &lt;code&gt;Supabase&lt;/code&gt; or other databases, create a &lt;code&gt;.env&lt;/code&gt; file and fill in the service address&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mJK7JPAF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/planetscale-connect-setting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mJK7JPAF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/planetscale-connect-setting.png" alt="https://cos.codefe.top/images/planetscale-connect-setting.png" width="800" height="718"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;DATABASE_URL='mysql://user:password@aws.connect.psdb.cloud/dev?sslaccept=strict'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relationship diagram can be seen in this visualization &lt;a href="https://prismaliser.app/"&gt;Prisma model website&lt;/a&gt; as follows, representing category instances and web link instances respectively&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1TwIu2cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/frontend-nav-prisma-model.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1TwIu2cm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/frontend-nav-prisma-model.png" alt="https://cos.codefe.top/images/frontend-nav-prisma-model.png" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Table Sync
&lt;/h3&gt;

&lt;p&gt;Then execute the command to synchronize the local model to the database table&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;npx prisma db push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you may encounter the following error&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vxxffx6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/prisma_db_push_error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vxxffx6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/prisma_db_push_error.png" alt="https://cos.codefe.top/images/prisma_db_push_error.png" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After checking the website, I found that the official website in &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-schema/relations/relation-mode#how-to-set-the-relation-mode-in-%20your-prisma-schema"&gt;documentation&lt;/a&gt; that the &lt;code&gt;MySQL&lt;/code&gt; database of &lt;a href="https://www.prisma.io/docs/guides/database/planetscale#differences-to-consider"&gt;PlanetScale&lt;/a&gt; does not support foreign keys, you need to specify &lt;code&gt;relationMode&lt;/code&gt; specially&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;datasource&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mysql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;relationMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prisma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default value of &lt;code&gt;relationMode&lt;/code&gt; is &lt;code&gt;foreignKeys&lt;/code&gt;, this option should be enabled when using the &lt;code&gt;MySQL&lt;/code&gt; connector of the &lt;code&gt;PlanetScale&lt;/code&gt; database to simulate relationships in the &lt;code&gt;Prisma Client&lt;/code&gt;. Execute the db push command again to synchronize the model to the database&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5sG-mnGK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/prisma_db_push.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5sG-mnGK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/prisma_db_push.png" alt="https://cos.codefe.top/images/prisma_db_push.png" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Insert and query data
&lt;/h3&gt;

&lt;p&gt;Then execute&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;npx prisma studio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the table editor to add your own data&lt;/p&gt;

&lt;p&gt;! &lt;a href="https://cos.codefe.top/images/frontend-studio.png"&gt;https://cos.codefe.top/images/frontend-studio.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Execute the command to generate the PrismaClient instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;pnpm install @prisma/client
npx prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can query the data by relation at once&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Prisma&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getNavLinks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;asc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;asc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CategoryWithLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PromiseReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;getNavLinks&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  User Authentication
&lt;/h2&gt;

&lt;p&gt;Integrating user authentication in &lt;code&gt;Next.js&lt;/code&gt; is very simple and can be done directly using &lt;code&gt;NextAuth&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  NextAuth
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;NextAuth&lt;/code&gt; is an open source authentication solution for &lt;code&gt;Next.js&lt;/code&gt; applications. By default, &lt;code&gt;NextAuth&lt;/code&gt; uses &lt;code&gt;JSON Web Tokens (JWT)&lt;/code&gt; to save user sessions. nextAuth supports popular login services such as &lt;code&gt;Google&lt;/code&gt;, &lt;code&gt;Facebook&lt;/code&gt;, &lt;code&gt;Auth0&lt;/code&gt;, &lt;code&gt;Apple&lt;/code&gt;, email, and &lt;code&gt;OAuth 1.0&lt;/code&gt; and &lt;code&gt;2.0&lt;/code&gt; services, among others, and is a flexible and configurable authentication solution.&lt;/p&gt;

&lt;p&gt;First install the required dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;pnpm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;next&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;adapter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add user-related models according to the official documentation guidelines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;Account&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;                 &lt;span class="nb"&gt;String&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cuid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;userId&lt;/span&gt;             &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;               &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;           &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;providerAccountId&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;refresh_token&lt;/span&gt;      &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;
  &lt;span class="nx"&gt;access_token&lt;/span&gt;       &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;
  &lt;span class="nx"&gt;expires_at&lt;/span&gt;         &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="nx"&gt;token_type&lt;/span&gt;         &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt;              &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="nx"&gt;id_token&lt;/span&gt;           &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;
  &lt;span class="nx"&gt;session_state&lt;/span&gt;      &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

  &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cascade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="p"&gt;@@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;providerAccountId&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;Session&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;           &lt;span class="nb"&gt;String&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cuid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;sessionToken&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;userId&lt;/span&gt;       &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;expires&lt;/span&gt;      &lt;span class="nx"&gt;DateTime&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt;         &lt;span class="nx"&gt;User&lt;/span&gt;     &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cascade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;            &lt;span class="nb"&gt;String&lt;/span&gt;    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cuid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;         &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;emailVerified&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;         &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="nx"&gt;accounts&lt;/span&gt;      &lt;span class="nx"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;sessions&lt;/span&gt;      &lt;span class="nx"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;VerificationToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;token&lt;/span&gt;      &lt;span class="nb"&gt;String&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;expires&lt;/span&gt;    &lt;span class="nx"&gt;DateTime&lt;/span&gt;

  &lt;span class="p"&gt;@@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  *&lt;strong&gt;&lt;em&gt;&lt;code&gt;GitHub&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt;*
&lt;/h3&gt;

&lt;p&gt;Obtain the &lt;code&gt;Github&lt;/code&gt; client &lt;code&gt;ID&lt;/code&gt;, &lt;a href="https://next-auth.js.org/providers/github"&gt;reference document&lt;/a&gt;, which contains the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;code&gt;GitHub&lt;/code&gt; application page and click Create &lt;code&gt;Github&lt;/code&gt; application.&lt;/p&gt;

&lt;p&gt;! &lt;a href="https://cos.codefe.top/images/github_setting_newapp.png"&gt;https://cos.codefe.top/images/github_setting_newapp.png&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter the name, homepage, and callback address after authorization. Here, fill in &lt;code&gt;localhost:3000&lt;/code&gt; for the development environment, and switch to your own online domain name when you go live.&lt;/p&gt;

&lt;p&gt;! &lt;a href="https://cos.codefe.top/images/github_setting_newapp_dev.png"&gt;https://cos.codefe.top/images/github_setting_newapp_dev.png&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click "Generate" and save &lt;code&gt;Client ID&lt;/code&gt; and &lt;code&gt;Client Secret&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Et7XQvUc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/github_clientid_dev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Et7XQvUc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/github_clientid_dev.png" alt="https://cos.codefe.top/images/github_clientid_dev.png" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Google&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Refer to the &lt;a href="https://next-auth.js.org/providers/google"&gt;NextAuth documentation&lt;/a&gt; guidelines at &lt;a href="https://console.developers.google.com/apis/credentials"&gt;Google developer site&lt;/a&gt; Register the application and select the &lt;code&gt;api&lt;/code&gt; service&lt;/p&gt;

&lt;p&gt;! &lt;a href="https://cos.codefe.top/images/google-oauth-demo.png"&gt;https://cos.codefe.top/images/google-oauth-demo.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;code&gt;Web&lt;/code&gt; application for the application type, similar to &lt;code&gt;Github&lt;/code&gt; fill in the trusted domain name and callback address to confirm&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DN1cdHE2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/google_oauth_setting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DN1cdHE2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/google_oauth_setting.png" alt="https://cos.codefe.top/images/google_oauth_setting.png" width="800" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create API Routing
&lt;/h3&gt;

&lt;p&gt;First add the authentication-related keys saved above to the &lt;code&gt;.env&lt;/code&gt; file, where &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt; is the user's key for generating &lt;code&gt;JWT&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;
&lt;span class="nx"&gt;GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;GOOGLE_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GOOGLE_CLIENT_SECRET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;github登录&lt;/span&gt;
&lt;span class="nx"&gt;GITHUB_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GITHUB_CLIENT_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;GITHUB_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GITHUB_CLIENT_SECRET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;NEXTAUTH_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;NEXTAUTH_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webnav&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the &lt;code&gt;API&lt;/code&gt; callbacks above are &lt;code&gt;/api/auth/github&lt;/code&gt; and &lt;code&gt;/api/auth/google&lt;/code&gt; respectively. Create the &lt;code&gt;app/api/auth/[...nestauth]/route.ts&lt;/code&gt; file, and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NextAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NextAuthOptions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GoogleProvider&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth/providers/google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GitHubProvider&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth/providers/github&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CredentialsProvider&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth/providers/credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@next-auth/prisma-adapter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compare&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bcrypt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextAuthOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;GitHubProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;GoogleProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;CredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Missing username or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="c1"&gt;// if user doesn't exist or password doesn't match&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid username or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jwt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NextAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Among them, &lt;code&gt;GitHubProvider&lt;/code&gt; user &lt;code&gt;Github&lt;/code&gt; login, &lt;code&gt;GoogleProvider&lt;/code&gt; for &lt;code&gt;Google&lt;/code&gt; login, &lt;code&gt;CredentialsProvider&lt;/code&gt; for custom login, here will check the email password, match it and return the user information.&lt;/p&gt;

&lt;p&gt;Also need to create the registration page, create &lt;code&gt;app/login/page.tsx&lt;/code&gt; file and &lt;code&gt;app/register/page.tsx&lt;/code&gt; file, page directly copied from &lt;strong&gt;&lt;a href="https://github.com/shadcn/taxonomy"&gt;taxonomy&lt;/a&gt;&lt;/strong&gt; page style, own Add &lt;code&gt;google&lt;/code&gt; login button, the effect is as shown&lt;/p&gt;

&lt;p&gt;! &lt;a href="https://cos.codefe.top/images/wennav-login-shot.png"&gt;https://cos.codefe.top/images/wennav-login-shot.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the page by&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signOut&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;signIn&lt;/code&gt; method allows logging in with a &lt;code&gt;Google&lt;/code&gt; or &lt;code&gt;GitHub&lt;/code&gt; account. &lt;code&gt;signOut&lt;/code&gt; allows logging out of a user session, see &lt;a href="https://next-auth.js.org/getting-started/client#signin"&gt;the official documentation for this section&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Import the project directly from &lt;code&gt;[Vercel](https://vercel.com/)&lt;/code&gt; and modify the build command to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before &lt;code&gt;build&lt;/code&gt;, otherwise the compilation will fail due to type error, and add the required environment variables in &lt;code&gt;.env&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Iz37j-Ud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/webnav-env-setting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Iz37j-Ud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/webnav-env-setting.png" alt="https://cos.codefe.top/images/webnav-env-setting.png" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the same time, because our data source is in the database, and &lt;code&gt;Nextjs&lt;/code&gt; is by default build-time page generation which is &lt;code&gt;SSG&lt;/code&gt; mode, we need to update the page content when the database has data updates, so we need to use its Incremental Static Generation &lt;code&gt;ISR (Incremental Static Regeneration)&lt;/code&gt; mode, refer to &lt;a href=""&gt;official documentation&lt;/a&gt; (&lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating#background-revalidation"&gt;https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating#background-revalidation&lt;/a&gt;), add the export in &lt;code&gt;page.tsx&lt;/code&gt;, here the update The timeliness requirement is not high, so set it to &lt;code&gt;1&lt;/code&gt; day&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;revalidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see from the build log that it has taken effect&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Kp1Mila--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/nextjs_vercel_deploy_isr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Kp1Mila--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cos.codefe.top/images/nextjs_vercel_deploy_isr.png" alt="https://cos.codefe.top/images/nextjs_vercel_deploy_isr.png" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After successful deployment, bind the custom domain name and finish sprinkling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This article to a simple navigation site for example, combined with &lt;code&gt;Next.js&lt;/code&gt;, &lt;code&gt;Prisma&lt;/code&gt;, &lt;code&gt;NextAuth&lt;/code&gt;, &lt;code&gt;shadcn/ui&lt;/code&gt; to learn how to build full-stack applications, you can open &lt;a href="https://webnav.codefe.top/"&gt;final website page&lt;/a&gt;, you can also in this &lt;a href="https://github.com/wangfengyuan/frontend-nav"&gt;project open source address&lt;/a&gt; to view the full code, wrir article is not easy, welcome star, thanks.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>prisma</category>
      <category>nextauth</category>
      <category>react</category>
    </item>
  </channel>
</rss>
