【Next.js】PostgreSQLで取得したデータをCardで表示させる方法2

# サンプル

このような画面をサーバからデータを取得・表示します。
![画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/a9bac8af-06f4-88a6-c7e8-c77185c1ac04.png)

# フロント側
コードを見て、いくつか注意すべきポイントがあります。
特に、商品リストを表示する部分で `map`メソッドの返り値が `Card`コンポーネントに渡す必要があります。

#### 1. 商品リストの表示部分の修正
`inventoryProducts.map`の中で、`Card`コンポーネントを返却します。
`map`は要素を返す必要があるので、返り値に return を追加する必要があります。

### 結論:
`map`メソッド内で `Card`コンポーネントを返すようにしました (return を追加)。
`inventoryProducts`が null の場合を考慮して、条件分岐を追加しました。
もし `inventoryProducts`が空でない場合、商品を表示し、空の場合には<b>「商品が見つかりませんでした」</b>と表示します。

全体のコードは下記となります。

```app/product/productList/page.tsx
'use client'//付けること
import ProductNavBar from "@/app/components/productHeaders/navbar/page"
import Card from "@/app/components/card/page"
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";

export default function ListOfProducts(){
    const router = useRouter();
    const [inventoryProducts,setInventoryProducts] = useState<any>(null);

    useEffect(()=>{
        const fetchEvent = async()=>{
            try{
                const productslist = await fetch('/api/products/readAllProducts',{
                    method:'GET'
                });

                const data = await productslist.json();
                console.log("productリストは、",data);
                setInventoryProducts(data);
                //console.log("商品の在庫は、",data);
                
                if(Array.isArray(data) && data.length > 0){
                    //商品リストがあれば登録する
                    setInventoryProducts(data);
                    console.log("setInventoryProductsのデータは、",setInventoryProducts(data));
                }else{
                    setInventoryProducts([]);
                }
                
            }catch(error){
                alert('商品リストの取得に失敗しました。');
            }
        };
        fetchEvent();
    },[]);

    console.log("在庫のJSONデータの全部のinventoryProductsは、",inventoryProducts);

    return (
        <div className="shadow-md border border-gray-500 rounded-md">
            <ProductNavBar></ProductNavBar>
            <main className="flex items-center justify-center w-full ml-4 mt-4 mb-4 mr-4">
                
                <div className="cardArea grid grid-cols-3 gap-4">
                    {inventoryProducts && inventoryProducts.length > 0 ? (
                        inventoryProducts.map((product)=>{
                            return <Card product={product} key={product.productId}></Card>
                        })
                    ):(
                        <p>商品が見つかりませんでした。</p>
                    )}
                </div>
                
            </main>
        </div>
    )
}
```
## Cardコンポーネントの作成
```app/components/card/page.tsx
'use client'//Next.jsはデフォルトはサーバ側の設定である
import { useRouter } from "next/navigation";

export default function Card({product}){
    //ルーティング設定
    const router = useRouter();

    //詳細を見るボタンクリックしたときの処理
    const clickProductDetail = ()=>{
        router.push('');
    }

    const {productId,productName,description,productImagepath,price,quantity} = product;
    return (
        <div>
            <div>
                <div>
                    <input type="hidden" name="productId" value={productId}/>
                </div>
            </div>
            <div>
                <figure>
                    <img src={productImagepath} alt="No Imagae"/>
                </figure>
                <div className="card-body">
                    <h2 className="card-title">{productName}</h2>
                    <p>{description}</p>
                    <p>{price}円(yen)</p>
                    <p>{quantity}</p>
                    <div>
                        <button
                            type="button" 
                            className="bg-blue-500 shadow-md text-white font-bold px-4 py-4 rounded-md"
                            onClick={clickProductDetail}
                            >
                            詳細を見る
                        </button>
                    </div>
                </div>
            </div>
        </div>
    )
}
```

# サーバ側
フロント画面へは、JSON形式で返却するようにしました。
```app/api/products/readAllProducts/route.ts
import { NextResponse } from "next/server";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function GET(){
    try{
        //商品をすべて取得する
        const allProductsList = await prisma.product.findMany({
            /*
            include:{
                items:true // Itemテーブルのデータを含めて取得
            }
            */
        });
        console.log("すべての商品：",allProductsList);
        //HTTP Status:500(データベース接続エラー)
        if(!allProductsList){
            return NextResponse.json({message:'商品が1つも登録されていません。',status:500});
        }
        return NextResponse.json(allProductsList);
    }catch(error){
        //HTTP Status503(サーバ停止またはメンテナンス中)
        return NextResponse.json({message:'データベース停止またはメンテナンス中です。',status:503});
    }
    
}
```

# スキーマ構造
スキーマも載せておきます。
```schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

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

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

model User{
  userId Int @id @default(autoincrement())
  userName String
  email String @unique
  password String
  createdAt DateTime @default(now())

  orders   Order[]  // 1人のユーザーは複数の注文を持つ
}

model AdminUser{
  adminUserId Int @id @default(autoincrement())
  adminUserName String
  adminEmail String @unique
  adminPassword String
  createdAt DateTime @default(now())

  products      Product[] // 1人の管理者は複数の商品を管理できる
}

model Product{
  productId Int @id @default(autoincrement())
  productName String
  quantity Int  @default(0) //売価
  price Int     @default(0) //仕入れ数量
  description String
  productImagepath String
  createdAt DateTime @default(now())
  updatedAt DateTime @default(now())

  items   Item[]   // 1つの製品は複数のアイテムに関連付けられる
  adminUserId  Int      // 管理者が商品を管理
  adminUser      AdminUser  @relation(fields: [adminUserId], references: [adminUserId]) // 管理者とのリレーション
}

model Item{
  itemId Int @id @default(autoincrement())
  quantity Int
  price Int
  createdAt DateTime @default(now())
  updatedAt DateTime @default(now())

  productId Int      // Product と 多対1 のリレーション
  product   Product  @relation(fields: [productId], references: [productId])

  orderId   Int      // Order と 1対多 のリレーション
  order     Order    @relation(fields: [orderId], references: [orderId])
}

model Order{
  orderId Int @id @default(autoincrement())
  total Int
  createdAt DateTime @default(now())
  updatedAt DateTime @default(now())

  userId    Int      // User と 1対多 のリレーション
  user      User     @relation(fields: [userId], references: [userId])
  items     Item[]   // 1つの注文には複数のアイテムが関連付けられる
}

```

以上です。