1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js】Prismaの使い方(findUnique編)

Posted at

記事作成の背景

ORMのPrismaでデータベースから一意のレコードをfindUniqueで取得する際に、沼ってしまったので備忘録として記載します。

沼った原因は何だったのか?

結論は、スキーマ定義で対象となるカラムに対してUnique属性を付与していなかったことが原因でした。
今回は、emailpasswordの2つで絞り込んで一意のレコードが取得できるようにします。

scema.prisma(修正前)
model Users{
  id Int @id @default(autoincrement())
  name String
  email String 
  password String 
  created_at String
  updated_at String
}

↑のスキーマ定義だとエラーが生じてしまいます。
コンソールには、「IDとは、一意となるカラムを少なくとも1つは指定してね」という表示が出ます。

なので、下記のように修正します。

scema.prisma(修正後)
model Users{
  id Int @id @default(autoincrement())
  name String
  email String @unique
  password String @unique
  created_at String
  updated_at String
}

これで一意のレコードを取得するためのスキーマ定義完成です。

今回のサンプルはこちら↓↓
emailとpasswordでデータベースからレコードが取得出来たら、toastでメッセージを表示する機能です。

LoginForm.js
'use client';

import {signIn} from 'next-auth/react';
import { useRouter } from 'next/router';
import toast from 'react-hot-toast';
import { useState } from 'react';
import { getLoginUser } from '../lib/actions/user.action';
import { redirect } from 'next/navigation';
import { revalidatePath } from 'next/cache';
import nextSession from "next-session";


export default function LoginForm({callbackUrl}){
    
    //const router = useRouter();
    const [email,setEmail] = useState('');
    const [password,setPassword] = useState('');

    return (
        <form onSubmit={handleSubmit} className='flex-1 space-y-6'>
            <div className='space-y-2'>
                <label className='label' htmlFor='email'>
                    email
                </label>
                <input
                    id='email'
                    className='input input-bordered w-full'
                    value={email}
                    onChange={e=>setEmail(e.target.value)}
                    type='email'
                    placeholder='Enter your email address'
                    required
                />
            </div>
            <div className='space-y-2'>
                <label className='label' htmlFor='password'>
                    Password
                </label>
                <input
                    id='password'
                    className='input input-bordered w-full'
                    value={password}
                    onChange={e=>setPassword(e.target.value)}
                    type='password'
                    placeholder='Enter password'
                    required
                    minLength={6}
                />
            </div>
            <button className='btn btn-primary btn-block mt-4' id='loginBtn'>Log In</button>
            <button className='btn btn-secondary btn-block mt-4' id='redirectToHome' disabled>
                <a href='http://localhost:3000/'>
                    ホーム画面へ
                </a>              
            </button>
        </form>
    )
}

const handleSubmit = async event=>{
    event.preventDefault();
    //指定したのページへ線維させるか判定するためのフラグ
    let redirectFlg = false;
    
    let email = document.getElementById('email').value;
    let password = document.getElementById('password').value;
    
    const res = await signIn('credentials',{
        email,
        password,
        redirect:false
    });
    //非同期通信
    var selectResult = await getLoginUser(email,password);
    
    sessionStorage.key='名前';
    sessionStorage.value = selectResult;
    sessionStorage.setItem('name',selectResult);
    const sessionData = sessionStorage.getItem('name');
    console.log('セッションに保存されている名前は、'+sessionStorage.getItem('name'));
    console.log('セッションに保存されている名前は、'+sessionData);
    
    
    
    if(res.status === 200){
        toast.success('Login Successful');
        //redirectFlg = true;
        redirectFlg = true;
        
    }else{
        if(res.error === 'CredentialSignin'){
            res.error = 'Wrong Password';
        }
        toast.error(`Login Failed:${res.error}`);
    }
    
    if(redirectFlg){
        //Loginボタンのオブジェクト
        let loginBtn = document.getElementById('loginBtn');
        let redirectToHome = document.getElementById('redirectToHome');
        loginBtn.disabled = true;
        redirectToHome.disabled = false;
    }else{
        //Loginボタンのオブジェクト
        let loginBtn = document.getElementById('loginBtn');
        let redirectToHome = document.getElementById('redirectToHome');
        loginBtn.disabled = false;
        redirectToHome.disabled = true;
    }
    
}



user.action.js
'use server';
import prisma from '../prisma';
import bcrypt from 'bcrypt';

export async function createUser(user){
    //パスワードをハッシュ化する
    const passwordHash = await bcrypt.hash(user.password,10);

    //作成日時を取得する
    let createdAtDate = getCreatedAtDate();

    //更新日時を取得する
    let updatedAtDate = getUpdatedAtDate();

    //登録処理を実行する
    const newUser = await prisma.users.create({data:{
        name:user.name,
        email:user.email,
        password:passwordHash,
        created_at:createdAtDate,
        updated_at:updatedAtDate,
        
    }});
    return newUser;
    /*
    const passwordHash = await bcrypt.hash(user.password,10);
    const newuser = await User.create({
        ...user,
        password:passwordHash
    });
    return JSON.parse(JSON.stringify(newuser));
    */
}

export async function getUserByEmail(email){
    const user = await prisma.users.findUnique({
        where:{
            email
        }
    });

    if(!user){
        throw new Error('User Not Found');
    }

    return JSON.parse(JSON.stringify(user));
}

/**
 * 作成日時を取得する<BR/>
 * 作成日時を取得する処理
 * @returns {String} 作成日時 getCreatedAtDate
 */
function getCreatedAtDate(){
    //作成日時を取得する
    const createdAt = new Date();
    createdAt.setTime(createdAt.getTime()-createdAt.getTimezoneOffset()*60*1000);
    const getCreatedAtDate = createdAt.toISOString().replace('T',' ').substring(0,19);
    //日付型を文字列に型変換して返却
    return getCreatedAtDate.toString();
}

/**
 * 更新日時を取得する<BR/>
 * 更新日時を取得する処理
 * @returns {String} 更新日時 getUpdatedAtDate
 */
function getUpdatedAtDate(){
    //更新日時を取得する
    const updatedAt = new Date();
    updatedAt.setTime(updatedAt.getTime()-updatedAt.getTimezoneOffset()*60*1000);
    const getUpdatedAtDate = updatedAt.toISOString().replace('T',' ').substring(0,19);
    //日付型を文字列の型変換して返却する
    return getUpdatedAtDate.toString();
}

export async function getLoginUser(email,password){
    var userInfo='';
    //パスワードをハッシュ化
    const passwordHash = await bcrypt.hash(password,10);
    console.log(passwordHash);
    userInfo = await prisma.users.findUnique({
        where:({
            email:email,
            password:'$2b$10$/qDzMuB0dQxMSabo3leDE.HH63JS2nbXwTb/POKs9digm4pP4DQD2',//password
        })
    });
    console.log('ユーザーの検索結果は:'+userInfo.name);
    //userInfo = JSON.parse(userInfo);
    return userInfo.name; //userInfo
}

以上です。

参考サイト

Prisma Client API reference

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?