Next.js Integration for SEO with SSR¶

NXTBN E-commerce GraphQL API Integration Guide¶

Table of Contents¶

Overview¶

This guide demonstrates how to integrate the NXTBN E-commerce GraphQL API with Next.js using server-side rendering (SSR) for optimal SEO performance. We’ll use getServerSideProps and getStaticProps where appropriate to ensure content is rendered on the server.

Setup¶

First, set up your GraphQL client configuration:

// lib/graphql-client.ts
import { GraphQLClient } from 'graphql-request';

const endpoint = process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || 'http://127.0.0.1:8000/graphql/';

export const graphqlClient = new GraphQLClient(endpoint);

Product Pages¶

Single Product Page¶

Create a dynamic product page with SSR:

// pages/products/[slug].tsx
import { GetServerSideProps } from 'next';
import { graphqlClient } from '@/lib/graphql-client';

const PRODUCT_QUERY = `
    query GetProduct($id: ID!) {
        product(id: $id) {
            id
            name
            summary
            description
            slug
            thumbnail
            metaTitle
            metaDescription
            defaultVariant {
                id
                name
                price
            }
        }
    }
`;

export const getServerSideProps: GetServerSideProps = async ({ params }) => {
    try {
        const { product } = await graphqlClient.request(PRODUCT_QUERY, {
            id: params?.slug
        });

        return {
            props: {
                product,
                seo: {
                    title: product.metaTitle || product.name,
                    description: product.metaDescription || product.summary,
                    openGraph: {
                        images: [{ url: product.thumbnail }]
                    }
                }
            }
        };
    } catch (error) {
        return { notFound: true };
    }
};

const ProductPage = ({ product, seo }) => {
    // Your product page component code
};

export default ProductPage;

Product Listing Page¶

Implement a paginated product listing with SSR:

// pages/products/index.tsx
import { GetServerSideProps } from 'next';

const PRODUCTS_QUERY = `
    query GetProducts($first: Int!, $after: String) {
        allProducts(first: $first, after: $after) {
            pageInfo {
                hasNextPage
                endCursor
            }
            edges {
                node {
                    id
                    name
                    summary
                    thumbnail
                    price
                }
            }
        }
    }
`;

export const getServerSideProps: GetServerSideProps = async ({ query }) => {
    const { page = 1 } = query;
    const first = 12; // Products per page

    try {
        const { allProducts } = await graphqlClient.request(PRODUCTS_QUERY, {
            first,
            after: ((page as number) - 1) * first || null
        });

        return {
            props: {
                products: allProducts.edges.map(({ node }) => node),
                pagination: {
                    hasMore: allProducts.pageInfo.hasNextPage,
                    endCursor: allProducts.pageInfo.endCursor
                }
            }
        };
    } catch (error) {
        return { notFound: true };
    }
};

Category Pages¶

Implement category pages with static paths and server-side props:

// pages/categories/[slug].tsx
import { GetStaticPaths, GetStaticProps } from 'next';

const CATEGORIES_QUERY = `
    query GetCategories {
        categoriesHierarchical {
            edges {
                node {
                    id
                    name
                    slug
                }
            }
        }
    }
`;

const CATEGORY_PRODUCTS_QUERY = `
    query GetCategoryProducts($categoryName: String!, $first: Int!) {
        allProducts(categoryName_Icontains: $categoryName, first: $first) {
            edges {
                node {
                    id
                    name
                    summary
                    thumbnail
                }
            }
        }
    }
`;

export const getStaticPaths: GetStaticPaths = async () => {
    const { categoriesHierarchical } = await graphqlClient.request(CATEGORIES_QUERY);
    
    const paths = categoriesHierarchical.edges.map(({ node }) => ({
        params: { slug: node.slug }
    }));

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

export const getStaticProps: GetStaticProps = async ({ params }) => {
    try {
        const { allProducts } = await graphqlClient.request(CATEGORY_PRODUCTS_QUERY, {
            categoryName: params?.slug,
            first: 12
        });

        return {
            props: {
                products: allProducts.edges.map(({ node }) => node)
            },
            revalidate: 60 // Revalidate every minute
        };
    } catch (error) {
        return { notFound: true };
    }
};

Search Implementation¶

Create a server-side search implementation:

// pages/search.tsx
import { GetServerSideProps } from 'next';

const SEARCH_QUERY = `
    query SearchProducts($search: String!, $first: Int!) {
        allProducts(search: $search, first: $first) {
            edges {
                node {
                    id
                    name
                    summary
                    thumbnail
                    price
                }
            }
        }
    }
`;

export const getServerSideProps: GetServerSideProps = async ({ query }) => {
    const { q: searchQuery, page = 1 } = query;
    const first = 12;

    if (!searchQuery) {
        return {
            props: {
                products: [],
                searchQuery: ''
            }
        };
    }

    try {
        const { allProducts } = await graphqlClient.request(SEARCH_QUERY, {
            search: searchQuery,
            first
        });

        return {
            props: {
                products: allProducts.edges.map(({ node }) => node),
                searchQuery
            }
        };
    } catch (error) {
        return { props: { products: [], searchQuery, error: 'Search failed' } };
    }
};

SEO Optimization¶

Implement SEO components for all pages:

// components/SEO.tsx
import Head from 'next/head';

interface SEOProps {
    title: string;
    description: string;
    image?: string;
    type?: string;
}

export const SEO = ({ title, description, image, type = 'website' }: SEOProps) => (
    <Head>
        <title>{title}</title>
        <meta name="description" content={description} />
        <meta property="og:title" content={title} />
        <meta property="og:description" content={description} />
        {image && <meta property="og:image" content={image} />}
        <meta property="og:type" content={type} />
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={description} />
        {image && <meta name="twitter:image" content={image} />}
    </Head>
);

Best Practices for SEO¶

  • Dynamic Metadata: Always use the product’s or category’s metaTitle and metaDescription when available.

// Example usage in product page
<SEO 
    title={product.metaTitle || product.name}
    description={product.metaDescription || product.summary}
    image={product.thumbnail}
    type="product"
/>
  • Structured Data: Implement JSON-LD for products:

// components/ProductStructuredData.tsx
const ProductStructuredData = ({ product }) => (
    <Head>
        <script
            type="application/ld+json"
            dangerouslySetInnerHTML={{
                __html: JSON.stringify({
                    '@context': 'https://schema.org',
                    '@type': 'Product',
                    name: product.name,
                    description: product.description,
                    image: product.thumbnail,
                    offers: {
                        '@type': 'Offer',
                        price: product.defaultVariant.priceRaw,
                        priceCurrency: 'USD', // Update according to your currency
                        availability: 'https://schema.org/InStock'
                    }
                })
            }}
        />
    </Head>
);

Performance Optimization¶

  • Use Image component from next/image for optimized images.

  • Implement proper caching strategies.

  • Use incremental static regeneration where appropriate.

// Example of image optimization
import Image from 'next/image';

const ProductImage = ({ product }) => (
    <Image
        src={product.thumbnail}
        alt={product.name}
        width={500}
        height={500}
        priority={true}
        className="product-image"
    />
);

Additional Recommendations¶

  • Use semantic HTML elements.

  • Implement proper error boundaries.

  • Add loading states for better UX.

  • Implement proper TypeScript interfaces for all data structures.

  • Use proper HTTP status codes for error handling.

  • Implement proper caching strategies using Next.js built-in features.

This integration ensures optimal SEO performance by:

  • Server-side rendering of all critical pages.

  • Proper metadata and structured data implementation.

  • Optimized image loading.

  • Clean URLs and proper routing.

  • Fast page loads through efficient data fetching.