🐈

【Better Auth】NextJs & tRPC 【#9 Better Auth Sign Up Page2】

に公開

【#9 Better Auth Sign Up Page2】

YouTube: https://youtu.be/L5OApFhTLL8
https://youtu.be/L5OApFhTLL8

今回は前回実装したフォームのボタンにサインアップ用の関数を設定します。
こちらは、auth.tsで設定した「drizzleAdapter」を経由して
データベースに登録しますので、tRPCは経由しません。

https://www.better-auth.com/docs/basic-usage#sign-up

src/components/auth/sign-up-view.tsx
'use client'

import { useState } from 'react'
import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'

import { authClient } from '@/lib/auth-client'

import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'

const formSchema = z
  .object({
    name: z.string().min(1, { message: 'Name is required' }),
    email: z.email(),
    password: z.string().min(1, { message: 'Password is required' }),
    confirmPassword: z
      .string()
      .min(1, { message: 'Confirm password is required' }),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Passwords do not match',
    path: ['confirmPassword'],
  })

export const SignUpView = () => {
  const [isPending, setIsPending] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      name: '',
      email: '',
      password: '',
      confirmPassword: '',
    },
  })

  const onSubmit = async (data: z.infer<typeof formSchema>) => {
    setError(null)
    setIsPending(true)

    await authClient.signUp.email(
      {
        name: data.name,
        email: data.email,
        password: data.password,
        callbackURL: '/',
      },
      {
        onSuccess: () => {
          setIsPending(false)
        },
        onError: ({ error }) => {
          setIsPending(false)
          setError(error.message)
        },
      }
    )
  }

  // const onSocialSignIn = async (provider: 'github' | 'google') => {
  //   setError(null)
  //   setIsPending(true)

  //   await authClient.signIn.social(
  //     {
  //       provider: provider,
  //       callbackURL: '/',
  //     },
  //     {
  //       onSuccess: () => {
  //         setIsPending(false)
  //       },
  //       onError: ({ error }) => {
  //         setIsPending(false)
  //         setError(error.message)
  //       },
  //     }
  //   )
  // }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)}>
        <div className="flex flex-col gap-y-6">
          <div className="flex flex-col items-center text-center gap-2">
            <h1 className="text-3xl font-bold">Sign Up</h1>
            <p className="text-muted-foreground text-balance">
              Create your account
            </p>
          </div>

          <FormField
            control={form.control}
            name="name"
            render={({ field }) => (
              <FormItem>
                <FormLabel>Name</FormLabel>
                <FormControl>
                  <Input type="text" placeholder="Enter your name" {...field} />
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          />

          <FormField
            control={form.control}
            name="email"
            render={({ field }) => (
              <FormItem>
                <FormLabel>Email</FormLabel>
                <FormControl>
                  <Input
                    type="text"
                    placeholder="Enter your email"
                    {...field}
                  />
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          />

          <FormField
            control={form.control}
            name="password"
            render={({ field }) => (
              <FormItem>
                <FormLabel>Password</FormLabel>
                <FormControl>
                  <Input
                    type="password"
                    placeholder="Enter your password"
                    {...field}
                  />
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          />

          <FormField
            control={form.control}
            name="confirmPassword"
            render={({ field }) => (
              <FormItem>
                <FormLabel>Confirm Password</FormLabel>
                <FormControl>
                  <Input
                    type="password"
                    placeholder="Enter your confirm password"
                    {...field}
                  />
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          />
          {!!error && <p>{error}</p>}
          <Button disabled={isPending} type="submit" className="w-full">
            Sign up
          </Button>
          {/* <Button
            disabled={isPending}
            type="button"
            onClick={() => onSocialSignIn('google')}
            className="w-full"
          >
            Google Sign up
          </Button> */}
        </div>
      </form>
    </Form>
  )
}

GoogleやGithubのOAuthを使用する際には
以下のファイルの「socialProviders」を有効化します。

src/lib/auth.ts
import { betterAuth } from 'better-auth'
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { db } from '@/db' // your drizzle instance
import * as schema from '@/db/schema'

export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
  },
  // socialProviders: {
  //   github: {
  //     clientId: process.env.GITHUB_CLIENT_ID as string,
  //     clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
  //   },
  // },
  database: drizzleAdapter(db, {
    provider: 'pg', // or "mysql", "sqlite"
    schema: {
      ...schema,
    },
  }),
})

Discussion