作成したログイン機能をNextjsに移す


## はじめに

以前作成した [ログインしたユーザーがTODOリストを作成できるようにする](https://qiita.com/idaten7240/items/f11db6a20985190e60fd) をNext.jsに移行します。


## next.jsとは

Next.jsは、Facebookが開発したReactベースのフレームワークです。サーバーサイドレンダリング（SSR）や静的サイト生成（SSG）を容易に実装できるよう設計されており、SEO対策やパフォーマンスの最適化にも効果的です。


## 準備
####  Node.js と npm のインストール
```
node -v
npm -v
```

作業ディレクトリに移動します（例：workspace）
```
cd ~/workspace
```

Reactプロジェクトの作成
```
npx create-react-app app todo
cd app todo
```






## ページコンポーネントの作成

HTMLファイルをNext.jsのページコンポーネント（.jsxファイル）に変換します。pagesディレクトリ内に以下のファイルを作成します

作成したフォルダは以下です

📦pages
 ┣ 📜contact.jsx
 ┣ 📜login.jsx
 ┣ 📜profile.jsx
 ┗ 📜todo.jsx
 ┗ 📜signup.jsx





## JSXへの変換

HTMLをJSXに変換する際、以下の変更が必要です

1. class属性をclassNameに変更します。
2. 自己閉じタグにはスラッシュを追加します（例：< img />）。
3. 属性値がJavaScriptの式である場合は、{}で囲みます（例：< input type="text" value={username} />）。



<details><summary>login.jsxファイル</summary>

```
import '../styles/styles.css'
import axios from 'axios'
import { useState } from 'react'
import { useRouter } from 'next/router'
import toastr from 'toastr'
import 'toastr/build/toastr.min.css'
import Link from 'next/link'

export default function Login() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const router = useRouter()

  const handleLogin = async (e) => {
    e.preventDefault()

    try {
      console.log(email)
      console.log(password)

      const response = await axios.post(
        'http://localhost:4000/api/users/login',
        { email, password },
      )

      console.log(response)
      toastr.success(response.data.message)
      localStorage.setItem('userId', response.data.userId)

      router.push('/profile')
    } catch (error) {
      toastr.error('メールアドレスまたはパスワードが正しくありません。')
    }
  }

  return (
    <div className="container">
      <h2>ログイン</h2>
      <form onSubmit={handleLogin}>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="メールアドレス"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="パスワード"
          required
        />
        <button type="submit">ログイン</button>
      </form>
      <p>
        アカウントをお持ちでない方は
        <Link href="/signup">新規アカウント作成</Link>
      </p>
    </div>
  )
}

```
</details>


<details><summary>signup.jsxファイル</summary>

```
// src/pages/signup.jsx
import '../styles/styles.css'

import { useState } from 'react'
import axios from 'axios'
import toastr from 'toastr'
import 'toastr/build/toastr.min.css'

export default function Signup() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [username, setUsername] = useState('')
  const [userId, setUserId] = useState('')
  const [userIcon, setUserIcon] = useState(null)

  const handleSubmit = async (e) => {
    e.preventDefault()

    const formData = new FormData()
    formData.append('image', userIcon)

    try {
      const imageResponse = await axios.post(
        `https://api.imgbb.com/1/upload?key=c0f0b6237007dc47943f085d13b621c1`,
        formData,
      )

      console.log(imageResponse)
      console.log(imageResponse.data.status)

      if (imageResponse.data.status === 200) {
        const imageUrl = imageResponse.data.data.url
        console.log(imageUrl)

        // ユーザー情報と画像URLをサーバーに送信
        const userResponse = await axios.post(
          'http://localhost:4000/api/users/register',
          {
            email,
            password,
            name: username,
            userId,
            userIcon: imageUrl,
          },
        )

        console.log(userResponse)
        console.log(userResponse.status)

        if (userResponse.status === 201) {
          toastr.success('アカウントが正常に作成されました。')
          window.location.href = '/login'
        } else {
          throw new Error('Failed to create user')
        }
      } else {
        throw new Error('Failed to upload image')
      }
    } catch (error) {
      console.error('Error during account creation:', error)
      toastr.error('アカウントの作成に失敗しました。')
    }
  }

  return (
    <div className="container">
      <h2>新規アカウント作成</h2>
      <form onSubmit={handleSubmit}>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="メールアドレス"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="パスワード"
          required
        />
        <input
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
          placeholder="ユーザー名"
          required
        />
        <input
          type="text"
          value={userId}
          onChange={(e) => setUserId(e.target.value)}
          placeholder="ユーザーID"
          required
        />
        <input
          type="file"
          onChange={(e) => setUserIcon(e.target.files[0])}
          accept="image/*"
        />
        <button type="submit">アカウント作成</button>
      </form>
    </div>
  )
}

```

</details>


<details><summary>profile.jsxファイル</summary>

```
// src/pages/profile.jsx
//モジュールのインストール
import '../styles/styles.css'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import Image from 'next/image'
import axios from 'axios'
import Link from 'next/link'

export default function Profile() {
//状態の初期化
  const [username, setUsername] = useState('')//ユーザー名を管理する状態
  const [userIcon, setUserIcon] = useState('')//ユーザーアイコンを管理する状態
  const router = useRouter()//Next.jsのルーターを初期化

  useEffect(() => {
    const fetchProfile = async () => {//ページが読み込まれたらfetchProfile関数が実行される　ストレージからユーザーIDを取得
      const userId = localStorage.getItem('userId')//ローカルストレージから保存されたユーザーIDを取得
      if (!userId) {
        router.push('/login')//ユーザーIDがない場合ログインページにリダイレクト
      } else {

        try {
          const response = await axios.get(
            `http://localhost:4000/api/users/${userId}`,//APIからユーザー情報を取得
          )
          const { name, userIcon } = response.data//取得したデータを分割代入
          setUsername(name)//取得したemailをsetUsernameにセットする
          setUserIcon(userIcon)//取得したuserIconをsetUserIconにセットする
        } catch (error) {
          console.error('プロフィール取得エラー:', error)// エラー時の処理
          router.push('/login')// ログインページにリダイレクト
        }
      }
    }

    fetchProfile()// ページが読み込まれたときに実行
  }, [router])

  return (
    <div className="container">
      <h2>
        ようこそ、<span>{username}</span>さん
      </h2>
      {userIcon && (
        <Image src={userIcon} alt="ユーザーアイコン" width={200} height={200} />
      )}
      <p>
        <Link href="/todo">TODOリストを管理</Link>
      </p>
      <p>
        <Link href="/contact">お問い合わせ</Link>
      </p>
    </div>
  )
}

```
</details>




<details><summary>todo.jsxファイル</summary>

```
// src/pages/todo.jsx
import '../styles/styles.css'
import { useState, useEffect } from 'react'
import { useRouter } from 'next/router'
import axios from 'axios'

export default function Todo() {
  const [tasks, setTasks] = useState([])
  const [taskName, setTaskName] = useState('')
  const [taskDescription, setTaskDescription] = useState('')
  const [taskDeadline, setTaskDeadline] = useState('')
  const [userId, setUserId] = useState('')
  const router = useRouter()

  useEffect(() => {
    fetchTasks()
  }, [])

  useEffect(() => {
    const fetchProfile = async () => {
      console.log(`fetchProfile()`)
      const userId = localStorage.getItem('userId')
      if (!userId) {
        router.push('/login')
      } else {
        setUserId(userId)
      }
    }

    fetchProfile()
    console.log(`userId = `, userId)
  }, [router])

  //タスク一覧を取得
  const fetchTasks = async () => {
    //関数の宣言　タスクをサーバーから取得する処理
    console.log(`fetchTasks()`)
    try {
      const response = await axios.get('http://localhost:4000/tasks') ///サーバーにリクエストを送信
      console.log(response)
      setTasks(response.data) //取得したデータをtasksに保存
    } catch (error) {
      console.error('Failed to fetch tasks:', error) //サーバーにリクエストを送るときにエラーが発生した場合エラーメッセージを表示
    }
  }

  //タスクを追加
  const handleSubmit = async () => {
    //新しいタスクを生成してサーバーに送信する処理
    console.log(`handleSubmit()`)
    console.log(`userId = `, userId)
    //ユーザーIDの確認

    if (!userId) {
      router.push('/login')
    } else {
      const newTask = {
        userId: Number(userId),
        title: taskName,
        description: taskDescription,
        deadline: taskDeadline,
      }

      console.log(`newTask =`, newTask)

      try {
        const response = await axios.post(
          'http://localhost:4000/tasks',
          newTask,
        )
        console.log(`response =`, response)

        setTasks([...tasks, response.data])
        setTaskName('')
        setTaskDescription('')
        setTaskDeadline('')
      } catch (error) {
        console.error('Failed to add task:', error)
      }
    }
  }

  //タスクの削除
  const handleDelete = async (taskId) => {
    try {
      await axios.delete(`http://localhost:4000/tasks/${taskId}`)
      const updatedTasks = tasks.filter((task) => task.id !== taskId)
      setTasks(updatedTasks)
    } catch (error) {
      console.error('Failed to delete task:', error)
    }
  }
  console.log(handleDelete)

  //コンポーネントの構成
  return (
    <div className="container">
      <h2>TODOリスト</h2>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={taskName}
          onChange={(e) => setTaskName(e.target.value)}
          placeholder="タスク名"
          required
        />
        <textarea
          value={taskDescription}
          onChange={(e) => setTaskDescription(e.target.value)}
          placeholder="タスク説明"
          required
        ></textarea>
        <input
          type="date"
          value={taskDeadline}
          onChange={(e) => setTaskDeadline(e.target.value)}
          required
        />
        <button type="submit">タスク追加</button>
      </form>
      <button onClick={() => router.push('/profile')} className="back-button">
        プロフィールに戻る
      </button>
      <div className="todo-list">
        {tasks.map((task, index) => (
          <div key={task.id || index}>
            <p>タスク名: {task.name}</p>
            <p>説明: {task.description}</p>
            <p>期限: {task.deadline}</p>
            <button onClick={() => handleDelete(task.id)}>削除</button>
          </div>
        ))}
      </div>
    </div>
  )
}

```
</details>




<details><summary>contact.jsxファイル</summary>

```
// src/pages/contact.jsx
import '../styles/styles.css'
import axios from 'axios'
// src/pages/Contact.jsx
import { useState } from 'react';

export default function Contact() {
  const [formData, setFormData] = useState({
    email: '',
    username: '',
    query: '',
  });

  const [isSubmitting, setIsSubmitting] = useState(false); // 送信中の状態を管理

  // フォームの入力が変更されたときの処理
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));
  };

  // フォーム送信処理
  const handleSubmit = async (e) => {
    e.preventDefault(); // デフォルトのフォーム送信を防止
    setIsSubmitting(true); // 送信中の状態に設定

    try {
      const response = await fetch('https://formspree.io/f/xpwavooz', {
        method: 'POST',
        body: new URLSearchParams(formData), // フォームデータを送信
        headers: {
          Accept: 'application/json',
        },
      });

      const data = await response.json();
      if (response.ok) {
        alert('お問い合わせありがとうございます！');
        setFormData({ email: '', username: '', query: '' }); // フォームをリセット
      } else {
        alert('送信に失敗しました。');
      }
    } catch (error) {
      alert('エラーが発生しました。');
    } finally {
      setIsSubmitting(false); // 送信ボタンを再び有効化
    }
  };

  return (
    <div className="container">
      <h2>お問い合わせ</h2>
      <form onSubmit={handleSubmit}>
        <label htmlFor="email">メールアドレス:</label>
        <input
          type="email"
          id="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
          required
        />
        <br />
        <label htmlFor="username">ユーザー名:</label>
        <input
          type="text"
          id="username"
          name="username"
          value={formData.username}
          onChange={handleChange}
          required
        />
        <br />
        <label htmlFor="query">問い合わせ内容:</label>
        <textarea
          id="query"
          name="query"
          value={formData.query}
          onChange={handleChange}
          required
          rows="10"
          cols="50"
        ></textarea>
        <br />
        <button type="submit" disabled={isSubmitting}>
          {isSubmitting ? '送信中...' : '送信'}
        </button>
      </form>
    </div>
  );
}

```


</details>







## スタイルの統合

CSSファイルをNext.jsプロジェクトに統合します。src/stylesディレクトリにstyles.cssファイルを保存し、各ページから以下のようにインポートします
```
// src/pages/login.jsx
import '../styles/styles.css';
```

<details><summary>styles.cssファイル</summary>

```
/* 基本設定 */
body {
    font-family: 'Poppins', sans-serif;
    background-color: #f5f5f5;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
  }

  /* コンテナのスタイル */
  .container {
    width: 100%;
    max-width: 350px;
    padding: 30px;
    background-color: white;
    border-radius: 10px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
    text-align: center;
  }

  /* 見出しスタイル */
  h2 {
    margin-bottom: 20px;
    font-size: 1.5rem;
    color: #333;
    font-weight: 600;
  }

  /* 入力フィールドのスタイル */
  input[type='email'],
  input[type='password'],
  input[type='text'],
  input[type='file'] {
    width: 80%;
    max-width: 280px;
    padding: 12px 15px;
    margin: 10px 0;
    border: 2px solid #ddd;
    border-radius: 8px;
    background-color: #f9f9f9;
    font-size: 1rem;
    transition:
      border 0.3s,
      background-color 0.3s;
  }

  /* フォーカス時のスタイル */
  input[type='email']:focus,
  input[type='password']:focus,
  input[type='text']:focus {
    border-color: #b654c5;
    background-color: white;
    outline: none;
    box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
  }

  /* ボタンのスタイル */
  button {
    width: 80%;
    max-width: 280px;
    padding: 12px;
    background-color: #b654c5;
    color: white;
    font-size: 1rem;
    font-weight: 600;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition:
      background-color 0.3s,
      box-shadow 0.3s;
  }

  /* ボタンホバー時のエフェクト */
  button:hover {
    background-color: #b654c5;
    box-shadow: 0 4px 12px rgba(0, 91, 179, 0.2);
  }

  /* リンクのスタイル */
  p a {
    color: #b654c5;
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease;
  }

  p a:hover {
    color: #b654c5;
  }

  /* ユーザーアイコンのスタイル */
  #displayIcon {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    object-fit: cover;
    margin-top: 10px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  }

  /* レスポンシブ設定 */
  @media (max-width: 768px) {
    .container {
      width: 90%;
    }
  }



  /* TODOリストのスタイル調整 */
  .todo-list {
    width: 100%;
    max-width: 350px;
    margin-top: 20px;
    padding: 10px;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  }

  .todo-list div {
    padding: 10px;
    margin: 5px 0;
    border-bottom: 1px solid #eee;
  }

  .todo-list div:last-child {
    border-bottom: none;
  }

  .todo-list p {
    margin: 5px 0;
    color: #333;
  }

  button {
    margin-top: 10px;
  }
```

</details>











# APIルートの設定（準備）


#### 開発環境の準備

Node.jsのインストール
Node.jsがまだインストールされていない場合は、Node.js公式サイトからインストーラをダウンロードしてインストールしてください。
[Node.js公式サイト](https://nodejs.org/en)



#### 新しいプロジェクトの作成

Express.jsとその他の依存関係を含む新しいプロジェクトを設定します。データベースとの接続にはMySQLドライバを使用し、環境変数の管理にはdotenvを導入します。

```
mkdir my-api
cd my-api
npm init -y
npm install express mysql dotenv
```


#### アプリケーションの実行
アプリケーションを設定し、以下のようにサーバーを起動します
```
node index.js
```
これで、基本的なAPIのセットアップが完了し、Express.jsとMySQLを使用して開発を進めることができます。

# APIルート

Next.jsのAPIルートを利用して、フォームの送信や外部APIとの通信をサーバーサイドで処理します。pages/apiディレクトリ内に必要なAPIルートファイルを作成します。

-------



#### ユーザー保存API



<details><summary>users.jsファイル</summary>


```//必要なモジュールをインポート
const express = require('express')
const router = express.Router()
const { PrismaClient } = require('@prisma/client')
const bcrypt = require('bcryptjs') // パスワードを暗号化するためのライブラリ
const prisma = new PrismaClient()



// 新規アカウント作成

//クライアントが POSTリクエストで /register にメールアドレス、名前、パスワードを送信。
//サーバーは、パスワードをハッシュ化し、データベースに新しいユーザーを登録。
//登録が成功したら、201（新規作成）とユーザーIDを返す。
//もしエラーが発生したら、500エラーを返す。

router.post('/register', async (req, res) => {//新しいユーザーが登録するAPIを定義する
  const { email, name, password, userIcon } = req.body//クライアントからメールアドレス、名前、パスワードを取得
console.log('email=',email)
console.log('name=',name)
console.log('password=',password)
console.log('userIcon=',userIcon)
console.log('req.body=',req.body)

  try {//例外が発生する可能性のある処理　try~catch文
    // パスワードのハッシュ化
    const salt = await bcrypt.genSalt(10) // パスワードのハッシュ化に必要な「塩」を生成
    const hashedPassword = await bcrypt.hash(password, salt) //塩を使って、パスワードをハッシュ化

    // 新しいユーザーをデータベースに保存
    const newUser = await prisma.user.create({//データベースに新しいユーザーを登録
      data: {
        email: email,//メールを保存
        name: name,//名前を保存
        password: hashedPassword, // ハッシュ化されたパスワードを保存
        userIcon: userIcon, // アイコンURLを保存//追加部分
      },

    })
    //ユーザー登録成功時のレスポンス
    res.status(201).json({//201(新規作成)を返す
      message: 'ユーザー登録に成功しました',
      userId: newUser.id, // 新しく作成されたユーザーのIDを返す
    })
  } catch (error) {//例外が発生した場合の処理　try~catch文
    console.error('ユーザー登録に失敗しました:', error)
    res.status(500).json({ message: 'ユーザー登録に失敗しました' })//500(サーバー内部エラー)を返す
  }
})




// 全ユーザーの取得

//クライアントが GETリクエストで / にアクセス。
//サーバーは、データベースからすべてのユーザー情報を取得。
//取得が成功したら、**200（成功）**とともにユーザー情報を返す。
//もしエラーが発生したら、500エラーを返す。


router.get('/', async (req, res) => {//全てのユーザー情報を取得するAPIを定義する
  try {//例外が発生する可能性のある処理　try~catch文
    const users = await prisma.user.findMany()//データベースに保存されている全てのユーザーを取得
    res.status(200).json(users)//200(成功)を返す、取得したユーザー情報をJSON形式で返す
  } catch (error) {//例外が発生した場合の処理　try~catch文
    console.error('ユーザー取得に失敗しました:', error)
    res.status(500).json({ message: 'ユーザー取得に失敗しました' })//500(サーバー内部エラー)を返しメッセージを表示
  }
})



// ユーザーログイン

//まとめ（処理の流れ）
//ユーザーがメールアドレスとパスワードをPOSTリクエストで送信します。
//サーバーはデータベースでそのユーザーを探します。
//ユーザーが見つかり、かつパスワードが正しい場合は、ログイン成功のメッセージを返します。
//ユーザーが見つからない、またはパスワードが間違っていた場合は、認証エラーメッセージを返します。
//エラーが発生した場合は、サーバー内部エラーのメッセージを返します。

router.post('/login', async (req, res) => {//ユーザーがログイン情報を送信するリクエストを受け付けます
  const { email, password } = req.body//ユーザーが入力したメールアドレスとパスワードを取り出す
  try {//例外が発生する可能性のある処理　try~catch文
    const user = await prisma.user.findUnique({
      where: { email: email },//データベースからメールアドレスが一致するユーザーを検索する
    })

    if (user && (await bcrypt.compare(password, user.password))) {//ユーザーが入力したパスワードとデータベースに保存されているハッシュ化されたパスワードを比較
      res.status(200).json({//200(成功)を返す
        message: 'ログイン成功',
        userId: user.id,
        userIcon: user.userIcon, // アイコンURLを追加
      })
    } else {//ログイン失敗時の処理
      res.status(401).json({ message: '認証情報が無効です' })//ユーザーが見つからないパスワードが間違っている場合401(認証失敗)を返す
    }
  } catch (error) {//例外が発生した場合の処理　try~catch文
    console.error('ログインに失敗しました:', error)//エラーが起きた時コンソールにエラーを表示する
    res.status(500).json({ message: 'ログインに失敗しました' })//500(サーバー内部エラー)を返しメッセージを表示
  }
})

module.exports = router




// 特定のユーザーをIDで取得する

//特定のIDに対応するユーザーをデータベースから探し出して返すAPIを作っています。
//ユーザーが見つかればその情報を返し、見つからなければ「404 エラー」を返します。
//エラーが発生した場合には、「500 エラー」とエラーメッセージを返します。

router.get('/:id', async (req, res) => {///api/users/:id にアクセスしたときユーザーの情報をデータベースから取得し返す
  const userId = parseInt(req.params.id) // IDを取得する　文字列を数値に変換
  try { //例外が発生する可能性のある処理
    const user = await prisma.user.findUnique({//データベースから特定のユーザーを探す
      where: { id: userId },//特定のIdに一致するユーザーを探す

    })

    if (user) {//データベースからユーザーが見つかった場合の処理
      res.status(200).json(user)//200(成功)を返す
    } else {//データベースからユーザーが見つからなかった場合の処理
      res.status(404).json({ message: 'ユーザーが見つかりませんでした' })//404(見つからない)を返す
    }
  } catch (error) { //例外が発生した場合の処理　try~catch文
    console.error('ユーザー取得に失敗しました:', error)//エラーメッセージをコンソールに表示
    res.status(500).json({ message: 'ユーザー取得に失敗しました' })//500(サーバー内部エラー)を返す
  }
})

module.exports = router
```

</details>






------------
#### タスク保存API

<details><summary>tasks.jsファイル</summary>



```
const express = require('express') //ライブラリをインポート
const router = express.Router() //リクエストの受け口
const { PrismaClient } = require('@prisma/client') //Prisma クライアントをインポート
const prisma = new PrismaClient() //PrismaClient のインスタンスを作成

// タスク保存のルート
router.post('/', async (req, res) => {
  console.log(`req.body = `, req.body)

  const { userId, title, description, deadline } = req.body //req.bodyから{}の中身を受け取る

  try {
    // ユーザーの存在確認
    const user = await prisma.user.findUnique({
      where: { id: userId },
    }) //指定されたuserIdが存在しているか確認

    console.log(`user = `, user)

    if (!user) {
      return res.status(404).json({ message: 'ユーザーが見つかりませんでした' })
    } //指定されたuserIdが存在しない場合404エラーを返す

    const newTask = await prisma.task.create({
      //ユーザーが存在する場合は新しいタスクをデータベースに保存
      data: {
        userId: userId,
        title: title,
        description: description,
        deadline: new Date(deadline),
      },
    })

    console.log(`newTask = `, newTask)

    res.status(201).json({
      //タスクが正常に作成されたら201ステータスで作成したタスクの情報を返す
      message: 'タスクが正常に作成されました',
      taskId: newTask.id,
      title: newTask.title,
      description: newTask.description,
      deadline: newTask.deadline,
    })
  } catch (error) {
    console.error('タスク作成に失敗しました:', error)
    res.status(500).json({ message: 'タスク作成に失敗しました' })
  }
})

// 特定のユーザーのすべてのタスクを取得するルート
router.get('/user/:userId', async (req, res) => {
  const userId = parseInt(req.params.userId)

  try {
    // ユーザーの存在確認
    const user = await prisma.user.findUnique({
      where: { id: userId },
    })

    if (!user) {
      return res.status(404).json({ message: 'ユーザーが見つかりませんでした' })
    }

    const tasks = await prisma.task.findMany({
      where: {
        userId: userId,
      },
    })

    res.status(200).json(tasks)
  } catch (error) {
    console.error('タスクの取得に失敗しました:', error)
    res.status(500).json({ message: 'タスクの取得に失敗しました' })
  }
})

// タスクをIDによって取得するルート
router.get('/:id', async (req, res) => {
  const taskId = parseInt(req.params.id) //文字列を整数に変換

  try {
    const task = await prisma.task.findUnique({
      where: {
        id: taskId,
      },
    })

    if (task) {
      res.status(200).json(task)
    } else {
      res.status(404).json({ message: 'タスクが見つかりませんでした' }) //指定されたuserIdがデータベースに存在しない場合404エラーを返す
    }
  } catch (error) {
    console.error('タスクの取得に失敗しました:', error)
    res.status(500).json({ message: 'タスクの取得に失敗しました' })
  }
})

// タスクをIDによって削除するルート
router.delete('/:id', async (req, res) => {
  const taskId = parseInt(req.params.id) //文字列を整数に変換

  try {
    const task = await prisma.task.delete({
      //タスクを削除する
      where: {
        id: taskId,
      },
    })

    res.status(200).json({
      //削除が成功したらそのタスクのIDを200ステータスで返す
      message: 'タスクが正常に削除されました',
      deletedTaskId: task.id,
    })
  } catch (error) {
    if (error.code === 'P2025') {
      //タスクを見つからない場合P2025エラー
      res.status(404).json({ message: 'タスクが見つかりませんでした' }) //404ステータスを返す
    } else {
      console.error('タスクの削除に失敗しました:', error)
      res.status(500).json({ message: 'タスクの削除に失敗しました' })
    }
  }
})

//タスクの詳細を取得する
router.get('/:id/details', async (req, res) => {
  const taskId = parseInt(req.params.id)

  try {
    // タスクをIDで検索し、関連するユーザー情報も取得する
    const task = await prisma.task.findUnique({
      where: {
        id: taskId,
      },
      include: {
        user: true, // ユーザー情報を一緒に取得
      },
    })

    if (!task) {
      return res.status(404).json({ message: 'タスクが見つかりませんでした' })
    }

    // タスクの詳細情報を返す
    res.status(200).json({
      taskId: task.id,
      title: task.title,
      description: task.description,
      deadline: task.deadline,
      user: {
        userId: task.user.id,
        userName: task.user.name,
        userEmail: task.user.email,
      },
    })
  } catch (error) {
    console.error('タスクの詳細取得に失敗しました:', error)
    res.status(500).json({ message: 'タスクの詳細取得に失敗しました' })
  }
})

// 全てのタスクを取得するAPI
router.get('/', async (req, res) => {
  try {
    // Prismaを使って全てのタスクを取得
    const tasks = await prisma.task.findMany() // タスクを全件取得

    // タスクが見つかった場合、200ステータスでデータを返す
    res.status(200).json(tasks)
  } catch (error) {
    console.error('すべてのタスクの取得に失敗しました:', error)
    res.status(500).json({ message: 'すべてのタスクの取得に失敗しました' })
  }
})

module.exports = router //このファイルで定義したルートを他のファイルで使えるようにする


```


</details>




-------


#### ndex.jsファイル

<details><summary>index.jsファイル</summary>

```
const express = require('express')
const { Pool } = require('pg')
require('dotenv').config()
const cors = require('cors')

const app = express() //app というサーバーを作るためのオブジェクト（インスタンス）を作る
// この行を追加
app.use(express.json()) //受け取ったデータをJSONに変換する
app.use(cors()) //他のウェブサイトやアプリからのアクセスを許可
const port = process.env.PORT || 4000 //サーバーが動作するポート番号を決める
const usersRouter = require('./controllers/users') //（users.js）を読み込んで、usersRouter で使えるようにする
const tasksRouter = require('./tasks/tasks') //（tasks.js）を読み込んで、tasksRouter で使えるようにする

app.use(express.json())
console.log(`test `) //コンソールにtestと表示させている

const pool = new Pool({
  connectionString: process.env.DATABASE_URL || '', //PostgreSQLに接続するための設定
})

console.log(process.env.DATABASE_URL)

app.get('/', async (req, res) => {
  /// というURLにアクセスしたときに動く処理
  try {
    const { rows } = await pool.query('SELECT current_database()')
    console.log(rows)
    res.send(`Server time is: ${JSON.stringify(rows, null, 2)}`)
  } catch (err) {
    res.status(500).send('Database error')
  }
})
app.use('/api/users', usersRouter) // /api/users で始まるリクエストを usersRouter に渡して処理
app.use('/tasks', tasksRouter) ///tasks で始まるリクエストを tasksRouter に渡して処理

app.listen(port, () => {
  console.log(`Server running on port ${port}`) //サーバーを指定したポート番号で動作させる
})

```

</details>



#### ルーターのエクスポート
全てのルート定義の後に以下の行を確認してください。
```
module.exports = router;
```

--------
#### schema.prisma ファイル


<details><summary>schema.prismaファイル</summary>

```
generator client {
  provider = "prisma-client-js"
}

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

model User {
  id       Int    @id @default(autoincrement())
  email    String @unique
  name     String?
  password String
  userIcon  String? // ユーザーアイコンのURL（省略可能）

  Task Task[]
}

model Task {
  id          Int      @id @default(autoincrement())
  userId      Int
  title       String
  description String?
  deadline    DateTime
  user        User     @relation(fields: [userId], references: [id])
}

```

</details>


---------