0
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?

電子商取引システムの構築と最適化 | 第2部: ShopifyとNext.jsの環境構築とAPI統合

Posted at

はじめに

ヘッドレスE-commerceアーキテクチャにおいて、ShopifyのバックエンドとNext.jsのフロントエンドを統合することは、スケーラブル高性能なオンラインストアを構築するための基盤です。この第2部では、Shopify Storefront APIAdmin APIを活用し、Next.jsプロジェクトをセットアップしてGraphQLでデータフェッチを行う方法を解説します。Vercelを使ったデプロイも含め、開発者向けにAPI統合環境構築の詳細を提供します。パフォーマンスと拡張性を重視した実践的なアプローチを、コードスニペットとともに紹介します。

開発環境のセットアップ

効率的な開発には、適切な環境構築が不可欠です。以下は、ShopifyNext.jsを使用するための準備手順です。

1. Shopifyストアの準備

Shopify開発ストアを作成し、API認証情報を取得します。

# Shopify開発ストアの作成
1. Shopify Partnerアカウントにログイン
2. 「ストア」→「開発ストアの追加」を選択
3. ストア名(例:my-ecommerce-dev)を入力
4. Storefront APIトークンを生成:
   - 「アプリ」→「アプリを管理」→「プライベートアプリを作成」
   - Storefront APIを有効化し、トークンをコピー
5. Admin APIトークンを取得(在庫や注文管理用)
   - Admin APIスコープを有効化(products, ordersなど)

環境変数の例(.env.local):

SHOPIFY_STOREFRONT_API_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxx
SHOPIFY_ADMIN_API_TOKEN=shpua_xxxxxxxxxxxxxxxxxxxx
SHOPIFY_STOREFRONT_API_ENDPOINT=https://my-ecommerce-dev.myshopify.com/api/2025-07/graphql.json

2. Next.jsプロジェクトの初期化

Next.jsプロジェクトをTypeScriptとTailwind CSSでセットアップします。

# Next.jsプロジェクトの作成
1. npx create-next-app@latest --ts my-ecommerce
2. cd my-ecommerce
3. npm install @shopify/storefront-api-client @apollo/client graphql tailwindcss postcss autoprefixer
4. npx tailwindcss init -p

Tailwind CSSの設定(tailwind.config.js):

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

CSS初期化(styles/globals.css):

@tailwind base;
@tailwind components;
@tailwind utilities;

3. Apollo Clientの設定

Apollo Clientを統合して、GraphQLクエリを効率的に管理します。

# Apollo Clientの依存関係
npm install @apollo/client graphql

Apollo Clientの初期化(lib/apollo-client.ts):

import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';

const httpLink = createHttpLink({
  uri: process.env.SHOPIFY_STOREFRONT_API_ENDPOINT,
  headers: {
    'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_STOREFRONT_API_TOKEN,
  },
});

export const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
});

Shopify Storefront APIの統合

Shopify Storefront APIを使用し、商品データやコレクションを取得します。以下は、商品リストを取得するサンプルクエリです。

1. 商品データのフェッチ

Next.jsのgetStaticPropsで商品データを取得:

// pages/index.tsx
import { GetStaticProps } from 'next';
import { gql } from '@apollo/client';
import { client } from '../lib/apollo-client';

const GET_PRODUCTS = gql`
  query GetProducts($first: Int!) {
    products(first: $first) {
      edges {
        node {
          id
          title
          handle
          priceRange {
            minVariantPrice {
              amount
              currencyCode
            }
          }
          images(first: 1) {
            edges {
              node {
                src
                altText
              }
            }
          }
        }
      }
    }
  }
`;

export const getStaticProps: GetStaticProps = async () => {
  const { data } = await client.query({
    query: GET_PRODUCTS,
    variables: { first: 10 },
  });

  return {
    props: {
      products: data.products.edges.map(({ node }: { node: any }) => node),
    },
    revalidate: 60, // ISR: 60秒ごとに再生成
  };
};

export default function Home({ products }: { products: any[] }) {
  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-6">商品リスト</h1>
      <div className="grid grid-cols-3 gap-4">
        {products.map((product) => (
          <div key={product.id} className="border p-4 rounded">
            <img src={product.images.edges[0]?.node.src} alt={product.images.edges[0]?.node.altText} className="w-full h-48 object-cover" />
            <h2 className="text-xl font-semibold">{product.title}</h2>
            <p>{product.priceRange.minVariantPrice.amount} {product.priceRange.minVariantPrice.currencyCode}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

このコードは、**Incremental Static Regeneration (ISR)**を使用して、商品データを静的に生成しつつ、定期的に更新します。

2. コレクションの取得

カテゴリ(コレクション)データを取得するクエリ例:

query GetCollections($first: Int!) {
  collections(first: $first) {
    edges {
      node {
        id
        title
        handle
        products(first: 5) {
          edges {
            node {
              id
              title
            }
          }
        }
      }
    }
  }
}

Next.jsのdynamic routesでコレクションごとのページを生成:

// pages/collections/[handle].tsx
import { GetStaticPaths, GetStaticProps } from 'next';
import { gql } from '@apollo/client';
import { client } from '../../lib/apollo-client';

const GET_COLLECTION = gql`
  query GetCollection($handle: String!) {
    collection(handle: $handle) {
      id
      title
      products(first: 10) {
        edges {
          node {
            id
            title
            handle
            priceRange {
              minVariantPrice {
                amount
                currencyCode
              }
            }
          }
        }
      }
    }
  }
`;

export const getStaticPaths: GetStaticPaths = async () => {
  const { data } = await client.query({
    query: gql`
      query GetCollectionHandles {
        collections(first: 10) {
          edges {
            node {
              handle
            }
          }
        }
      }
    `,
  });

  const paths = data.collections.edges.map(({ node }: { node: any }) => ({
    params: { handle: node.handle },
  }));

  return { paths, fallback: 'blocking' };
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const { data } = await client.query({
    query: GET_COLLECTION,
    variables: { handle: params?.handle },
  });

  return {
    props: { collection: data.collection },
    revalidate: 60,
  };
};

export default function CollectionPage({ collection }: { collection: any }) {
  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-6">{collection.title}</h1>
      <div className="grid grid-cols-3 gap-4">
        {collection.products.edges.map(({ node }: { node: any }) => (
          <div key={node.id} className="border p-4 rounded">
            <h2 className="text-xl font-semibold">{node.title}</h2>
            <p>{node.priceRange.minVariantPrice.amount} {node.priceRange.minVariantPrice.currencyCode}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

このコードは、コレクションごとの動的ページを生成し、SEOパフォーマンスを最適化します。

Vercelでのデプロイ

Vercelを使用して、プロジェクトをグローバルにデプロイします。

# Vercelデプロイ手順
1. npm install -g vercel
2. cd my-ecommerce
3. vercel
4. 環境変数を設定:
   - SHOPIFY_STOREFRONT_API_TOKEN
   - SHOPIFY_STOREFRONT_API_ENDPOINT
5. ドメインを割り当て(例:my-ecommerce.vercel.app)
6. 自動スケーリングとエッジキャッシュを有効化

Vercel設定例(vercel.json):

{
  "framework": "nextjs",
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/next"
    }
  ],
  "env": {
    "SHOPIFY_STOREFRONT_API_ENDPOINT": "@shopify_storefront_api_endpoint",
    "SHOPIFY_STOREFRONT_API_TOKEN": "@shopify_storefront_api_token"
  }
}

テストとデバッグ

統合後、以下のテストを実施:

  • APIテスト: GraphQLクエリのレスポンス時間(目標:200ms以下)。
  • データ整合性: 商品データが正しくレンダリングされるか。
  • デプロイ確認: Vercelでページが正常に表示されるか。
  • エラーハンドリング: 無効なAPIトークンやクエリエラーの処理。

Apollo Clientのエラーハンドリング例:

// lib/apollo-client.ts
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { onError } from '@apollo/client/link/error';

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message }) => console.error(`[GraphQL error]: ${message}`));
  }
  if (networkError) console.error(`[Network error]: ${networkError}`);
});

const httpLink = createHttpLink({
  uri: process.env.SHOPIFY_STOREFRONT_API_ENDPOINT,
  headers: {
    'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_STOREFRONT_API_TOKEN,
  },
});

export const client = new ApolloClient({
  link: errorLink.concat(httpLink),
  cache: new InMemoryCache(),
});

まとめ

この第2部では、ShopifyNext.jsを使ったヘッドレスE-commerceの環境構築とStorefront APIの統合を解説しました。GraphQLApollo Clientを活用し、Vercelでデプロイすることで、スケーラブルな基盤を構築しました。次の第3部では、Next.jsTailwind CSSを使ったUI設計を掘り下げます。

Qiitaの皆さん、この記事が役に立ったら「いいね」や「ストック」をお願いします!コメントで技術的な質問や提案もお待ちしています!

0
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
0
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?