choblog

未分類

2025/03/29

2025/04/10

キャッチアップ画像

headless-wordpress (4) データフェッチ

~ 目次 ~

    1 記事作成

    まず新規投稿を2〜3作成、それぞれタイトル、本文、アイキャッチ画像を設定しておきます。

    この時リンクにかなが含まれていると読み込み時にエラーとなるので、特にタイトルに全角文字が含まれている場合は気をつけましょう。

    右サイドバーの投稿タブから、リンク部分をクリックして変更しておきます。

    2 WPGraphqlインストール

    wordpressのpluginから、wpgraphqlと検索してインストール。有効化したらサイドバーメニューからwpgraphqlのIDEを開いて以下を入力

    query postsListQuery {
      posts {
        edges {
          node {
            date
            excerpt
            id
            modified
            slug
            title
            categories {
              edges {
                node {
                  name
                  slug
                }
              }
            }
            featuredImage {
              node {
                sourceUrl
              }
            }
          }
        }
      }
    }

    左上のボタンを押してデータが取れていることを確認。

    3 frontend-nextからデータフェッチ

    まずはデータフェッチの流れを一度一つのファイルに並べてみました、あとでリファクタリング、別ファイルに分けます。

    import Image from 'next/image'
    
    // データフェッチ関数
    async function fetchGraphQL(
        query: string,
        variables: Record<string, unknown> = {},
    ) {
        try {
            const res = await fetch('http://backend-wp/graphql', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ query, variables }),
            })
            const json = await res.json()
            return json.data
        } catch (error) {
            console.error('Fetch failed:', error)
            return null
        }
    }
    
    // 型
    type postsListResponseType = {
        posts: {
            edges: {
                node: {
                    date: string
                    excerpt: string
                    id: string
                    modified: string
                    slug: string
                    title: string
                    categories: {
                        edges: {
                            node: {
                                name: string
                                slug: string
                            }
                        }[]
                    }
                    featuredImage: {
                        node: {
                            sourceUrl: string
                        }
                    }
                }
            }[]
        }
    }
    
    // データフェッチ関数の引数に渡すクエリ
    const postsListQuery = `
    query postsListQuery {
      posts {
        edges {
          node {
            date
            excerpt
            id
            modified
            slug
            title
            categories {
              edges {
                node {
                  name
                  slug
                }
              }
            }
            featuredImage {
              node {
                sourceUrl
              }
            }
          }
        }
      }
    }
    
    `
    
    // 入れ子になってるデータを取り出しやすく加工
    async function getPostsList() {
        try {
            const res: postsListResponseType = await fetchGraphQL(postsListQuery)
            return res.posts.edges.map((data) => {
                const posts = {
                    date: data.node.date,
                    excerpt: data.node.excerpt,
                    id: data.node.id,
                    modified: data.node.modified,
                    slug: data.node.slug,
                    title: data.node.title,
                    categories: {
                        name: data.node.categories.edges[0].node.name,
                        slug: data.node.categories.edges[0].node.slug,
                    },
                    featuredImage: {
                        sourceUrl: data.node.featuredImage.node.sourceUrl,
                    },
                }
                return posts
            })
        } catch (error) {
            console.error('Error in getPostsList:', error)
            throw error
        }
    }
    
    // 加工したデータを取得して表示
    export default async function Home() {
        const posts = await getPostsList()
    
        return (
            <div className="grid gap-3">
                <h1 className="border border-red-500">headless-wordpress</h1>
                <ul className="grid gap-3 border border-blue-500 p-3">
                    {posts.map((post) => {
                        return (
                            <li key={post.id} className="border border-yellow-500">
                                <h2>{post.title}</h2>
                                <div className="relative h-24 w-24">
                                    <Image
                                        priority
                                        fill
                                        className="object-cover"
                                        src={post.featuredImage.sourceUrl}
                                        alt="キャッチアップ画像"
                                        unoptimized
                                    />
                                </div>
                                <div
                                    dangerouslySetInnerHTML={{
                                        __html: post.excerpt,
                                    }}
                                ></div>
                            </li>
                        )
                    })}
                </ul>
            </div>
        )
    }
    

    リファクタリング、次のようにしてみました。

    frontend-next/
    ├── .env
    └── src/
        ├── api/
        │   └── fetchGraphQL.ts
        ├── app/
        │   └── page.tsx
        ├── query/
        │   └── postsListQuery.ts
        ├── data/
        │   └── getPostsList.ts
        └── type/
            └── postsListResponse.ts

    .env

    NEXT_PUBLIC_WPGRAPHQL_ENDPOINT=http://backend-wp/graphql
    

    fetchGraphQL.ts

    export async function fetchGraphQL(
        query: string,
        variables: Record<string, unknown> = {},
    ) {
        try {
            const res = await fetch(`${process.env.NEXT_PUBLIC_WPGRAPHQL_ENDPOINT}`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ query, variables }),
            })
            const json = await res.json()
            return json.data
        } catch (error) {
            console.error('Fetch failed:', error)
            return null
        }
    }
    
    

    page.tsx

    import Image from 'next/image'
    import { getPostsList } from '../data/getPostsList'
    
    export default async function Home() {
        const posts = await getPostsList()
    
        return (
            <div className="grid gap-3">
                <h1 className="border border-red-500">headless-wordpress</h1>
                <ul className="grid gap-3 border border-blue-500 p-3">
                    {posts.map((post) => {
                        return (
                            <li key={post.id} className="border border-yellow-500">
                                <h2>{post.title}</h2>
                                <div className="relative h-24 w-24">
                                    <Image
                                        priority
                                        fill
                                        className="object-cover"
                                        src={post.featuredImage.sourceUrl}
                                        alt="キャッチアップ画像"
                                        unoptimized
                                    />
                                </div>
                                <div
                                    dangerouslySetInnerHTML={{
                                        __html: post.excerpt,
                                    }}
                                ></div>
                            </li>
                        )
                    })}
                </ul>
            </div>
        )
    }
    

    postsListQuery.ts

    export const postsListQuery = `
    query postsListQuery {
      posts {
        edges {
          node {
            date
            excerpt
            id
            modified
            slug
            title
            categories {
              edges {
                node {
                  name
                  slug
                }
              }
            }
            featuredImage {
              node {
                sourceUrl
              }
            }
          }
        }
      }
    }
    `
    

    getPostsList.ts

    import { fetchGraphQL } from '../api/fetchGraphQL'
    import { postsListResponseType } from '../type/postsListResponse'
    import { postsListQuery } from '../query/postsListQuery'
    
    export async function getPostsList() {
        try {
            const res: postsListResponseType = await fetchGraphQL(postsListQuery)
            return res.posts.edges.map((data) => {
                const posts = {
                    date: data.node.date,
                    excerpt: data.node.excerpt,
                    id: data.node.id,
                    modified: data.node.modified,
                    slug: data.node.slug,
                    title: data.node.title,
                    categories: {
                        name: data.node.categories.edges[0].node.name,
                        slug: data.node.categories.edges[0].node.slug,
                    },
                    featuredImage: {
                        sourceUrl: data.node.featuredImage.node.sourceUrl,
                    },
                }
                return posts
            })
        } catch (error) {
            console.error('Error in getPostsList:', error)
            throw error
        }
    }
    
    

    postsListResponse.ts

    export type postsListResponseType = {
        posts: {
            edges: {
                node: {
                    date: string
                    excerpt: string
                    id: string
                    modified: string
                    slug: string
                    title: string
                    categories: {
                        edges: {
                            node: {
                                name: string
                                slug: string
                            }
                        }[]
                    }
                    featuredImage: {
                        node: {
                            sourceUrl: string
                        }
                    }
                }
            }[]
        }
    }
    

    広告

    広告

    広告

    広告

    広告

    広告