SEO GRAPHQL RANK MATH (ok)

Source Code

Step 1: Thử lấy seo với post

Bước 1: Tạo Dự Án Next.js mới

Chạy lệnh sau trong terminal để tạo một ứng dụng Next.js mới sử dụng TypeScript:

npx create-next-app@latest my-nextjs-app --typescript

Bước 3: Cài đặt Apollo Client và các thư viện cần thiết

Cài đặt Apollo Client, GraphQL và các thư viện cần thiết khác:

npm install @apollo/client graphql next-i18next

Bước 4: Thiết lập Apollo Client

Tạo một tệp apollo-client.ts trong thư mục nguồn (ví dụ: src hoặc ở cấp độ gốc tùy thuộc vào cách bạn tổ chức dự án):

pages\apollo-client.ts

// apollo-client.ts
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
const httpLink = createHttpLink({
  uri: 'https://test1.com/graphql', // Địa chỉ GraphQL của WordPress
});
const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
});
export default client;

Bước 5: Cấu hình i18n

Tạo một tệp next-i18next.config.js tại thư mục gốc của dự án:

next-i18next.config.js

// next-i18next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'vi'],
    defaultLocale: 'en',
  },
};

Sau đó thêm vào next.config.js để tích hợp i18n:

next.config.ts

const { i18n } = require('./next-i18next.config');
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
  i18n,
  reactStrictMode: true,
};
export default nextConfig;

Bước 6: Tạo Trang với SEO Metadata

Tạo tệp [...slug].tsx trong thư mục pages:

pages\[...slug].tsx

import { gql } from '@apollo/client';
import client from './apollo-client';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
interface SeoMetadata {
  title: string;
  description: string;
  focusKeywords: string;
}
interface PageProps {
  seo: SeoMetadata;
}
const GET_SEO_METADATA = gql`
  query GetSeoMetadata($slug: ID!) {
    page(id: $slug, idType: URI) {
      seo {
        title
        description
        focusKeywords
      }
    }
  }
`;
const Page: React.FC<PageProps> = ({ seo }) => {
  return (
    <div>
      <Head>
        <title>{seo.title}</title>
        <meta name="description" content={seo.description} />
        <meta name="keywords" content={seo.focusKeywords} />
      </Head>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
  );
};
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  const slugArray = Array.isArray(params?.slug) ? params.slug : [];
  const slug = slugArray.join('/');
  const { data } = await client.query({
    query: GET_SEO_METADATA,
    variables: { slug },
  });
  if (!data || !data.page) {
    return {
      notFound: true,
    };
  }
  return {
    props: {
      seo: data.page.seo,
    },
  };
};
export default Page;

Bước 7: Khởi động Dự Án

Chạy ứng dụng Next.js bằng lệnh:

npm run dev

— Bổ sung tuyệt vời sử dụng hàm vừa lấy nội dung và lấy seo

pages\[...slug].tsx

import { gql } from '@apollo/client';
import client from './apollo-client';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
interface SeoMetadata {
  title: string;
  description: string;
  focusKeywords: string;
}
interface PageProps {
  seo: SeoMetadata;
  page: {
    title: string;
    content: string;
  }
}
export const GET_SEO_METADATA = gql`
  query GetSeoMetadata($slug: ID!) {
    page(id: $slug, idType: URI) {
      title
      content
      seo {
        title
        description
        focusKeywords
      }
    }
  }
`;
const Page: React.FC<PageProps> = ({ seo,page }) => {
  console.log(page.content);
  return (
    <div>
      <Head>
        <title>{seo.title}</title>
        <meta name="description" content={seo.description} />
        <meta name="keywords" content={seo.focusKeywords} />
      </Head>
      <div dangerouslySetInnerHTML={{__html: page.content}}></div>
    </div>
  );
};
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  const slugArray = Array.isArray(params?.slug) ? params.slug : [];
  const slug = slugArray.join('/');
  const { data } = await client.query({
    query: GET_SEO_METADATA,
    variables: { slug },
  });
  if (!data || !data.page) {
    return {
      notFound: true,
    };
  }
  return {
    props: {
      seo: data.page.seo,
      page: data.page
    },
  };
};
export default Page;

— Cập nhật code cho blog sử dụng chung interface

pages\[...slug].tsx

import { gql } from '@apollo/client';
import client from './apollo-client';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
export interface SeoMetadata {
  title: string;
  description: string;
  focusKeywords: string;
}
export  interface PageProps {
  seo: SeoMetadata;
  page: {
    title: string;
    content: string;
  }
}
export const GET_SEO_METADATA = gql`
  query GetSeoMetadata($slug: ID!) {
    page(id: $slug, idType: URI) {
      title
      content
      seo {
        title
        description
        focusKeywords
      }
    }
  }
`;
const Page: React.FC<PageProps> = ({ seo,page }) => {
  console.log(page.content);
  return (
    <div>
      <Head>
        <title>{seo.title}</title>
        <meta name="description" content={seo.description} />
        <meta name="keywords" content={seo.focusKeywords} />
      </Head>
      <div dangerouslySetInnerHTML={{__html: page.content}}></div>
    </div>
  );
};
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  const slugArray = Array.isArray(params?.slug) ? params.slug : [];
  const slug = slugArray.join('/');
  const { data } = await client.query({
    query: GET_SEO_METADATA,
    variables: { slug },
  });
  if (!data || !data.page) {
    return {
      notFound: true,
    };
  }
  return {
    props: {
      seo: data.page.seo,
      page: data.page
    },
  };
};
export default Page;

Step 2 Thử lấy Seo với post

pages\posts\[slug].tsx

import { gql } from '@apollo/client';
import client from '../apollo-client'; // Đảm bảo đường dẫn chính xác
import { GetServerSideProps } from 'next';
import Head from 'next/head';
interface SeoMetadata {
  title: string;
  description: string;
  focusKeywords: string;
}
interface Post {
  seo: SeoMetadata;
  title: string;
  content: string;
}
interface PageProps {
  post: Post | null; // Null nếu không tìm thấy
}
const GET_POST_METADATA = gql`
  query GetPostMetadata($slug: ID!) {
    post(id: $slug, idType: SLUG) {
      title
      content
      seo {
        title
        description
        focusKeywords
      }
    }
  }
`;
const PostPage: React.FC<PageProps> = ({ post }) => {
  if (!post) {
    return <div>Không tìm thấy bài viết.</div>; // Hiển thị thông báo nếu bài viết không tồn tại
  }
  return (
    <div>
      <Head>
        <title>{post.seo.title}</title>
        <meta name="description" content={post.seo.description} />
        <meta name="keywords" content={post.seo.focusKeywords} />
      </Head>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </div>
  );
};
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  const slug = params?.slug as string; // Đảm bảo rằng slug là string
  const { data } = await client.query({
    query: GET_POST_METADATA,
    variables: { slug },
  });
  if (!data || !data.post) {
    return {
      notFound: true, // Trả về 404 nếu không tìm thấy bài viết
    };
  }
  return {
    props: {
      post: data.post, // Truyền dữ liệu bài viết vào props
    },
  };
};
export default PostPage;

Step 3 Thử lấy Seo với category

pages\categories\[slug].tsx

import { gql } from '@apollo/client';
import client from '../apollo-client'; // Đảm bảo đường dẫn chính xác
import { GetServerSideProps } from 'next';
import Head from 'next/head';
interface SeoMetadata {
  title: string;
  description: string;
  focusKeywords: string;
}
interface Post {
  title: string;
  slug: string;
}
interface Category {
  name: string;
  seo: SeoMetadata;
  posts: {
    nodes: Post[]; // danh sách bài viết
  };
}
interface GeneralSettings {
  title: string;
  description: string;
}
interface CategoryMetadataResponse {
  generalSettings: GeneralSettings;
  category: Category;
}
interface PageProps {
  category: Category | null; // Null nếu không tìm thấy
}
const GET_CATEGORY_METADATA = gql`
  query GetCategoryMetadata($slug: ID!) {
    category(id: $slug, idType: SLUG) {
      name
      seo {
        title
        description
        focusKeywords
      }
      posts {
        nodes {
          title
          slug
        }
      }
    }
  }
`;
const CategoryPage: React.FC<PageProps> = ({ category }) => {
  if (!category) {
    return <div>Không tìm thấy danh mục.</div>; // Hiển thị thông báo nếu danh mục không tồn tại
  }
  return (
    <div>
      <Head>
        <title>{category.seo.title}</title>
        <meta name="description" content={category.seo.description} />
        <meta name="keywords" content={category.seo.focusKeywords} />
      </Head>
      <h1>{category.name}</h1>
      <h2>Bài viết trong danh mục:</h2>
      <ul>
        {category.posts.nodes?.map(post => (
          <li key={post.slug}>
            <a href={`/posts/${post.slug}`}>{post.title}</a>
          </li>
        ))}
      </ul>
    </div>
  );
};
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  const slug = params?.slug as string; // Đảm bảo rằng slug là string
  const { data } = await client.query<CategoryMetadataResponse>({
    query: GET_CATEGORY_METADATA,
    variables: { slug },
  });
  if (!data || !data.category) {
    return {
      notFound: true, // Trả về 404 nếu không tìm thấy danh mục
    };
  }
  return {
    props: {
      category: data.category, // Truyền dữ liệu danh mục vào props
    },
  };
};
export default CategoryPage;

Step 4 Thử lấy Seo với Tag

pages\tags\[slug].tsx

import { gql } from '@apollo/client';
import client from '../apollo-client'; // Đảm bảo đường dẫn chính xác
import { GetServerSideProps } from 'next';
import Head from 'next/head';
interface SeoMetadata {
  title: string;
  description: string;
  focusKeywords: string;
}
interface Post {
  title: string;
  slug: string;
}
interface Tag {
  name: string;
  seo: SeoMetadata;
  posts: {
    nodes: Post[]; // danh sách bài viết
  };
}
interface GeneralSettings {
  title: string;
  description: string;
}
interface TagMetadataResponse {
  generalSettings: GeneralSettings;
  tag: Tag;
}
const GET_TAG_METADATA = gql`
  query GetTagMetadata($slug: ID!) {
    tag(id: $slug, idType: SLUG) {
      name
      seo {
        title
        description
        focusKeywords
      }
      posts {
        nodes {
          title
          slug
        }
      }
    }
  }
`;
const TagPage: React.FC<{ tag: Tag | null }> = ({ tag }) => {
  if (!tag) {
    return <div>Không tìm thấy thẻ.</div>; // Hiển thị thông báo nếu thẻ không tồn tại
  }
  return (
    <div>
      <Head>
        <title>{tag.seo.title}</title>
        <meta name="description" content={tag.seo.description} />
        <meta name="keywords" content={tag.seo.focusKeywords} />
      </Head>
      <h1>{tag.name}</h1>
      <h2>Bài viết trong thẻ:</h2>
      <ul>
        {tag.posts.nodes?.map(post => (
          <li key={post.slug}>
            <a href={`/posts/${post.slug}`}>{post.title}</a>
          </li>
        ))}
      </ul>
    </div>
  );
};
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
  const slug = params?.slug as string; // Đảm bảo rằng slug là string
  const { data } = await client.query<TagMetadataResponse>({
    query: GET_TAG_METADATA,
    variables: { slug },
  });
  if (!data || !data.tag) {
    return {
      notFound: true, // Trả về 404 nếu không tìm thấy thẻ
    };
  }
  return {
    props: {
      tag: data.tag, // Truyền dữ liệu thẻ vào props
    },
  };
};
export default TagPage;

Step 5.1 Thử lấy Seo với blog

pages\blog\index.tsx

import { gql } from '@apollo/client';
import client from '../apollo-client'; // Đảm bảo đường dẫn chính xác
import Head from 'next/head';
interface SeoMetadata {
  title: string;
  description: string;
}
interface Post {
  title: string;
  slug: string;
}
interface BlogPostsResponse {
  posts: {
    nodes: Post[];
  };
}
const GET_BLOG_POSTS = gql`
  query GetBlogPosts {
    posts {
      nodes {
        title
        slug
      }
    }
  }
`;
const BlogPage: React.FC<{ posts: Post[] }> = ({ posts }) => {
  return (
    <div>
      <Head>
        <title>Blog của tôi</title>
        <meta name="description" content="Chào mừng bạn đến với blog của tôi." />
      </Head>
      <h1>Danh sách bài viết:</h1>
      <ul>
        {posts.map(post => (
          <li key={post.slug}>
            <a href={`/posts/${post.slug}`}>{post.title}</a>
          </li>
        ))}
      </ul>
    </div>
  );
};
export const getServerSideProps = async () => {
  const { data } = await client.query<BlogPostsResponse>({
    query: GET_BLOG_POSTS,
  });
  return {
    props: {
      posts: data.posts.nodes, // Truyền danh sách bài viết vào props
    },
  };
};
export default BlogPage;

Step 5.2 Thử lấy Seo với blog (Full)

import { gql } from '@apollo/client';
import client from '../apollo-client'; // Đảm bảo đường dẫn chính xác
import Head from 'next/head';
import { GET_SEO_METADATA } from '../[...slug]';
import { GetServerSideProps } from 'next';
interface SeoMetadata {
  title: string;
  description: string;
  focusKeywords: string;
}
interface Post {
  title: string;
  slug: string;
}
interface BlogPostsResponse {
  posts: {
    nodes: Post[];
  };
  blog: SeoMetadata
}
const GET_BLOG_POSTS = gql`
  query GetBlogPosts {
    posts {
      nodes {
        title
        slug
      }
    }
  }
`;
const BlogPage: React.FC<{ posts: Post[],page: {seo: SeoMetadata} }> = ({ posts,page }) => {
  return (
    <div>
      <Head>
        <title>{page.seo.title}</title>
        <meta name="description" content={page.seo.description} />
        <meta name="keywords" content={page.seo.focusKeywords} />
      </Head>
      <h1>Danh sách bài viết:</h1>
      <ul>
        {posts.map(post => (
          <li key={post.slug}>
            <a href={`/posts/${post.slug}`}>{post.title}</a>
          </li>
        ))}
      </ul>
    </div>
  );
};
export const getServerSideProps: GetServerSideProps = async (context) => {
  const { data } = await client.query<BlogPostsResponse>({
    query: GET_BLOG_POSTS,
  });
  const lang = context.locale || context.params?.lang as string;
  const slug = lang === 'en' ? 'blog-en' : `blog`;
  const { data:blog } = await client.query({
    query: GET_SEO_METADATA,
    variables: { slug },
  });
  return {
    props: {
      posts: data.posts.nodes, // Truyền danh sách bài viết vào props
      page:blog.page
    },
  };
};
export default BlogPage;

Last updated

Was this helpful?