aboutsummaryrefslogtreecommitdiffstats
path: root/src/services/graphql/fetchers
diff options
context:
space:
mode:
Diffstat (limited to 'src/services/graphql/fetchers')
-rw-r--r--src/services/graphql/fetchers/comments/fetch-comments.ts65
-rw-r--r--src/services/graphql/fetchers/comments/index.ts1
-rw-r--r--src/services/graphql/fetchers/index.ts4
-rw-r--r--src/services/graphql/fetchers/posts/fetch-all-posts-slugs.ts34
-rw-r--r--src/services/graphql/fetchers/posts/fetch-last-post-cursor.ts37
-rw-r--r--src/services/graphql/fetchers/posts/fetch-post.ts92
-rw-r--r--src/services/graphql/fetchers/posts/fetch-posts-count.ts43
-rw-r--r--src/services/graphql/fetchers/posts/fetch-posts-list.ts97
-rw-r--r--src/services/graphql/fetchers/posts/fetch-recent-posts.ts76
-rw-r--r--src/services/graphql/fetchers/posts/index.ts6
-rw-r--r--src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts34
-rw-r--r--src/services/graphql/fetchers/thematics/fetch-thematic.ts96
-rw-r--r--src/services/graphql/fetchers/thematics/fetch-thematics-count.ts43
-rw-r--r--src/services/graphql/fetchers/thematics/fetch-thematics-list.ts78
-rw-r--r--src/services/graphql/fetchers/thematics/index.ts4
-rw-r--r--src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts34
-rw-r--r--src/services/graphql/fetchers/topics/fetch-topic.ts97
-rw-r--r--src/services/graphql/fetchers/topics/fetch-topics-count.ts43
-rw-r--r--src/services/graphql/fetchers/topics/fetch-topics-list.ts84
-rw-r--r--src/services/graphql/fetchers/topics/index.ts4
20 files changed, 972 insertions, 0 deletions
diff --git a/src/services/graphql/fetchers/comments/fetch-comments.ts b/src/services/graphql/fetchers/comments/fetch-comments.ts
new file mode 100644
index 0000000..85ae6c1
--- /dev/null
+++ b/src/services/graphql/fetchers/comments/fetch-comments.ts
@@ -0,0 +1,65 @@
+import type {
+ GraphQLCommentWhere,
+ GraphQLEdgesInput,
+ GraphQLNodes,
+ Nullable,
+ WPComment,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type CommentsListResponse = {
+ comments: Nullable<GraphQLNodes<WPComment>>;
+};
+
+const commentsListQuery = `query CommentsList($first: Int, $contentId: ID, $contentName: String, $status: String) {
+ comments(
+ first: $first
+ where: {contentId: $contentId, contentName: $contentName, order: ASC, orderby: COMMENT_DATE, status: $status}
+ ) {
+ nodes {
+ approved
+ author {
+ node {
+ avatar {
+ height
+ url
+ width
+ }
+ name
+ url
+ }
+ }
+ content
+ databaseId
+ date
+ parentDatabaseId
+ status
+ }
+ }
+}`;
+
+export type FetchCommentsListInput = Pick<GraphQLEdgesInput, 'first'> & {
+ where?: GraphQLCommentWhere;
+};
+
+/**
+ * Retrieve the comments list.
+ *
+ * @param {FetchCommentsListInput} input - The input to retrieve comments.
+ * @returns {Promise<WPComment[]>} An array of comments.
+ */
+export const fetchCommentsList = async ({
+ where,
+ ...vars
+}: FetchCommentsListInput): Promise<WPComment[]> => {
+ const response = await fetchGraphQL<CommentsListResponse>({
+ query: commentsListQuery,
+ url: getGraphQLUrl(),
+ variables: { ...vars, ...where },
+ });
+
+ if (!response.comments)
+ return Promise.reject(new Error('No comments found.'));
+
+ return response.comments.nodes;
+};
diff --git a/src/services/graphql/fetchers/comments/index.ts b/src/services/graphql/fetchers/comments/index.ts
new file mode 100644
index 0000000..6a15970
--- /dev/null
+++ b/src/services/graphql/fetchers/comments/index.ts
@@ -0,0 +1 @@
+export * from './fetch-comments';
diff --git a/src/services/graphql/fetchers/index.ts b/src/services/graphql/fetchers/index.ts
new file mode 100644
index 0000000..f45b1c0
--- /dev/null
+++ b/src/services/graphql/fetchers/index.ts
@@ -0,0 +1,4 @@
+export * from './comments';
+export * from './posts';
+export * from './thematics';
+export * from './topics';
diff --git a/src/services/graphql/fetchers/posts/fetch-all-posts-slugs.ts b/src/services/graphql/fetchers/posts/fetch-all-posts-slugs.ts
new file mode 100644
index 0000000..28f2bbf
--- /dev/null
+++ b/src/services/graphql/fetchers/posts/fetch-all-posts-slugs.ts
@@ -0,0 +1,34 @@
+import type { GraphQLNodes, Nullable, SlugNode } from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+import { fetchPostsCount } from './fetch-posts-count';
+
+type PostsSlugsResponse = {
+ posts: Nullable<GraphQLNodes<SlugNode>>;
+};
+
+const postsSlugsQuery = `query PostsSlugs($first: Int) {
+ posts(first: $first) {
+ nodes {
+ slug
+ }
+ }
+}`;
+
+/**
+ * Retrieve the WordPress posts slugs.
+ *
+ * @returns {Promise<string[]>} The posts slugs.
+ */
+export const fetchAllPostsSlugs = async (): Promise<string[]> => {
+ const postsCount = await fetchPostsCount();
+ const response = await fetchGraphQL<PostsSlugsResponse>({
+ query: postsSlugsQuery,
+ url: getGraphQLUrl(),
+ variables: { first: postsCount },
+ });
+
+ if (!response.posts)
+ return Promise.reject(new Error('Unable to find the posts slugs.'));
+
+ return response.posts.nodes.map((node) => node.slug);
+};
diff --git a/src/services/graphql/fetchers/posts/fetch-last-post-cursor.ts b/src/services/graphql/fetchers/posts/fetch-last-post-cursor.ts
new file mode 100644
index 0000000..d5ed174
--- /dev/null
+++ b/src/services/graphql/fetchers/posts/fetch-last-post-cursor.ts
@@ -0,0 +1,37 @@
+import type { GraphQLPageInfo, Nullable } from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type LastPostCursorResponse = {
+ posts: Nullable<{
+ pageInfo: Pick<GraphQLPageInfo, 'endCursor'>;
+ }>;
+};
+
+const lastPostCursorQuery = `query LastPostCursor($first: Int) {
+ posts(first: $first) {
+ pageInfo {
+ endCursor
+ }
+ }
+}`;
+
+/**
+ * Retrieve the cursor of the last post for a given number of posts.
+ *
+ * @param {number} count - The number of posts to fetch.
+ * @returns {Promise<string>} The cursor of the last post.
+ */
+export const fetchLastPostCursor = async (count: number): Promise<string> => {
+ const response = await fetchGraphQL<LastPostCursorResponse>({
+ url: getGraphQLUrl(),
+ query: lastPostCursorQuery,
+ variables: { first: count },
+ });
+
+ if (!response.posts?.pageInfo.endCursor)
+ return Promise.reject(
+ new Error('Unable to find the cursor of the last post.')
+ );
+
+ return response.posts.pageInfo.endCursor;
+};
diff --git a/src/services/graphql/fetchers/posts/fetch-post.ts b/src/services/graphql/fetchers/posts/fetch-post.ts
new file mode 100644
index 0000000..53c6bc3
--- /dev/null
+++ b/src/services/graphql/fetchers/posts/fetch-post.ts
@@ -0,0 +1,92 @@
+import type { Nullable, WPPost } from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type PostResponse = {
+ post: Nullable<WPPost>;
+};
+
+const postQuery = `query Post($slug: ID!) {
+ post(id: $slug, idType: SLUG) {
+ acfPosts {
+ postsInThematic {
+ ... on Thematic {
+ databaseId
+ slug
+ title
+ }
+ }
+ postsInTopic {
+ ... on Topic {
+ databaseId
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ slug
+ title
+ }
+ }
+ }
+ author {
+ node {
+ name
+ }
+ }
+ commentCount
+ contentParts {
+ afterMore
+ beforeMore
+ }
+ databaseId
+ date
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ info {
+ wordsCount
+ }
+ modified
+ seo {
+ metaDesc
+ title
+ }
+ slug
+ title
+ }
+}`;
+
+/**
+ * Retrieve a WordPress post by slug.
+ *
+ * @param {string} slug - The post slug.
+ * @returns {Promise<WPPost>} The requested post.
+ */
+export const fetchPost = async (slug: string): Promise<WPPost> => {
+ const response = await fetchGraphQL<PostResponse>({
+ query: postQuery,
+ url: getGraphQLUrl(),
+ variables: { slug },
+ });
+
+ if (!response.post)
+ return Promise.reject(
+ new Error(`No post found for the following slug ${slug}.`)
+ );
+
+ return response.post;
+};
diff --git a/src/services/graphql/fetchers/posts/fetch-posts-count.ts b/src/services/graphql/fetchers/posts/fetch-posts-count.ts
new file mode 100644
index 0000000..a72af52
--- /dev/null
+++ b/src/services/graphql/fetchers/posts/fetch-posts-count.ts
@@ -0,0 +1,43 @@
+import type {
+ GraphQLPageInfo,
+ GraphQLPostWhere,
+ Nullable,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type PostsCountResponse = {
+ posts: Nullable<{
+ pageInfo: Pick<GraphQLPageInfo, 'total'>;
+ }>;
+};
+
+const postsCountQuery = `query PostsCount($authorName: String, $search: String, $title: String) {
+ posts(where: {authorName: $authorName, search: $search, title: $title}) {
+ pageInfo {
+ total
+ }
+ }
+}`;
+
+/**
+ * Retrieve the total of WordPress posts.
+ *
+ * @param {GraphQLPostWhere} [input] - The input to filter the posts.
+ * @returns {Promise<number>} The total number of posts.
+ */
+export const fetchPostsCount = async (
+ input?: GraphQLPostWhere
+): Promise<number> => {
+ const response = await fetchGraphQL<PostsCountResponse>({
+ query: postsCountQuery,
+ url: getGraphQLUrl(),
+ variables: { ...input },
+ });
+
+ if (!response.posts)
+ return Promise.reject(
+ new Error('Unable to find the total number of posts.')
+ );
+
+ return response.posts.pageInfo.total;
+};
diff --git a/src/services/graphql/fetchers/posts/fetch-posts-list.ts b/src/services/graphql/fetchers/posts/fetch-posts-list.ts
new file mode 100644
index 0000000..452892b
--- /dev/null
+++ b/src/services/graphql/fetchers/posts/fetch-posts-list.ts
@@ -0,0 +1,97 @@
+import type {
+ GraphQLConnection,
+ GraphQLEdgesInput,
+ GraphQLPostOrderBy,
+ GraphQLPostWhere,
+ Nullable,
+ WPPostPreview,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type PostsListResponse = {
+ posts: Nullable<GraphQLConnection<WPPostPreview>>;
+};
+
+const postsListQuery = `query PostsList($after: String, $before: String, $first: Int, $last: Int, $authorName: String, $orderby: [PostObjectsConnectionOrderbyInput], $search: String, $title: String) {
+ posts(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: {authorName: $authorName, orderby: $orderby, search: $search, title: $title}
+ ) {
+ edges {
+ cursor
+ node {
+ acfPosts {
+ postsInThematic {
+ ... on Thematic {
+ databaseId
+ slug
+ title
+ }
+ }
+ }
+ commentCount
+ contentParts {
+ beforeMore
+ }
+ databaseId
+ date
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ info {
+ wordsCount
+ }
+ modified
+ slug
+ title
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ total
+ }
+ }
+}`;
+
+export type FetchPostsListInput = GraphQLEdgesInput & {
+ orderBy?: GraphQLPostOrderBy;
+ where?: GraphQLPostWhere;
+};
+
+/**
+ * Retrieve a paginated list of WordPress posts.
+ *
+ * @param {FetchPostsListInput} input - The input to retrieve posts.
+ * @returns {Promise<GraphQLConnection<WPPostPreview>>} The paginated posts.
+ */
+export const fetchPostsList = async ({
+ orderBy,
+ where,
+ ...vars
+}: FetchPostsListInput): Promise<GraphQLConnection<WPPostPreview>> => {
+ const response = await fetchGraphQL<PostsListResponse>({
+ query: postsListQuery,
+ url: getGraphQLUrl(),
+ variables: {
+ ...vars,
+ ...where,
+ orderBy: orderBy ? [orderBy] : undefined,
+ },
+ });
+
+ if (!response.posts) return Promise.reject(new Error('No posts found.'));
+
+ return response.posts;
+};
diff --git a/src/services/graphql/fetchers/posts/fetch-recent-posts.ts b/src/services/graphql/fetchers/posts/fetch-recent-posts.ts
new file mode 100644
index 0000000..12785d6
--- /dev/null
+++ b/src/services/graphql/fetchers/posts/fetch-recent-posts.ts
@@ -0,0 +1,76 @@
+import type {
+ GraphQLConnection,
+ GraphQLEdgesInput,
+ GraphQLPostWhere,
+ Nullable,
+ RecentWPPost,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type RecentPostsResponse = {
+ posts: Nullable<GraphQLConnection<RecentWPPost>>;
+};
+
+const recentPostsQuery = `query RecentPosts($after: String, $before: String, $first: Int, $last: Int, $authorName: String, $search: String, $title: String) {
+ posts(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: {authorName: $authorName, search: $search, title: $title, orderby: {field: DATE, order: DESC}}
+ ) {
+ edges {
+ cursor
+ node {
+ databaseId
+ date
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ slug
+ title
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ total
+ }
+ }
+}`;
+
+export type FetchRecentPostsInput = GraphQLEdgesInput & {
+ where?: GraphQLPostWhere;
+};
+
+/**
+ * Retrieve a paginated list of recent WordPress posts.
+ *
+ * @param {FetchRecentPostsInput} input - The input to retrieve recent posts.
+ * @returns {Promise<GraphQLConnection<RecentWPPost>>} The recent posts.
+ */
+export const fetchRecentPosts = async ({
+ where,
+ ...vars
+}: FetchRecentPostsInput): Promise<GraphQLConnection<RecentWPPost>> => {
+ const response = await fetchGraphQL<RecentPostsResponse>({
+ query: recentPostsQuery,
+ url: getGraphQLUrl(),
+ variables: { ...vars, ...where },
+ });
+
+ if (!response.posts)
+ return Promise.reject(new Error('No recent posts found.'));
+
+ return response.posts;
+};
diff --git a/src/services/graphql/fetchers/posts/index.ts b/src/services/graphql/fetchers/posts/index.ts
new file mode 100644
index 0000000..fd725cd
--- /dev/null
+++ b/src/services/graphql/fetchers/posts/index.ts
@@ -0,0 +1,6 @@
+export * from './fetch-all-posts-slugs';
+export * from './fetch-last-post-cursor';
+export * from './fetch-post';
+export * from './fetch-posts-count';
+export * from './fetch-posts-list';
+export * from './fetch-recent-posts';
diff --git a/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts b/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts
new file mode 100644
index 0000000..739c009
--- /dev/null
+++ b/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts
@@ -0,0 +1,34 @@
+import type { GraphQLNodes, Nullable, SlugNode } from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+import { fetchThematicsCount } from './fetch-thematics-count';
+
+type ThematicsSlugsResponse = {
+ thematics: Nullable<GraphQLNodes<SlugNode>>;
+};
+
+const thematicsSlugsQuery = `query ThematicsSlugs($first: Int) {
+ thematics(first: $first) {
+ nodes {
+ slug
+ }
+ }
+}`;
+
+/**
+ * Retrieve the WordPress thematics slugs.
+ *
+ * @returns {Promise<string[]>} The thematics slugs.
+ */
+export const fetchAllThematicsSlugs = async (): Promise<string[]> => {
+ const thematicsCount = await fetchThematicsCount();
+ const response = await fetchGraphQL<ThematicsSlugsResponse>({
+ query: thematicsSlugsQuery,
+ url: getGraphQLUrl(),
+ variables: { first: thematicsCount },
+ });
+
+ if (!response.thematics)
+ return Promise.reject(new Error('Unable to find the thematics slugs.'));
+
+ return response.thematics.nodes.map((node) => node.slug);
+};
diff --git a/src/services/graphql/fetchers/thematics/fetch-thematic.ts b/src/services/graphql/fetchers/thematics/fetch-thematic.ts
new file mode 100644
index 0000000..a9958bc
--- /dev/null
+++ b/src/services/graphql/fetchers/thematics/fetch-thematic.ts
@@ -0,0 +1,96 @@
+import type { Nullable, WPThematic } from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type ThematicResponse = {
+ thematic: Nullable<WPThematic>;
+};
+
+const thematicQuery = `query Thematic($slug: ID!) {
+ thematic(id: $slug, idType: SLUG) {
+ acfThematics {
+ postsInThematic {
+ ... on Post {
+ acfPosts {
+ postsInTopic {
+ ... on Topic {
+ databaseId
+ slug
+ title
+ }
+ }
+ }
+ author {
+ node {
+ name
+ }
+ }
+ commentCount
+ contentParts {
+ beforeMore
+ }
+ databaseId
+ date
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ info {
+ wordsCount
+ }
+ modified
+ slug
+ title
+ }
+ }
+ }
+ contentParts {
+ afterMore
+ beforeMore
+ }
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ seo {
+ metaDesc
+ title
+ }
+ slug
+ title
+ }
+}`;
+
+/**
+ * Retrieve a WordPress thematic by slug.
+ *
+ * @param {string} slug - The thematic slug.
+ * @returns {Promise<WPThematic>} The requested thematic.
+ */
+export const fetchThematic = async (slug: string): Promise<WPThematic> => {
+ const response = await fetchGraphQL<ThematicResponse>({
+ query: thematicQuery,
+ url: getGraphQLUrl(),
+ variables: { slug },
+ });
+
+ if (!response.thematic)
+ return Promise.reject(
+ new Error(`No thematic found for the following slug ${slug}.`)
+ );
+
+ return response.thematic;
+};
diff --git a/src/services/graphql/fetchers/thematics/fetch-thematics-count.ts b/src/services/graphql/fetchers/thematics/fetch-thematics-count.ts
new file mode 100644
index 0000000..29a3b17
--- /dev/null
+++ b/src/services/graphql/fetchers/thematics/fetch-thematics-count.ts
@@ -0,0 +1,43 @@
+import type {
+ GraphQLPageInfo,
+ GraphQLTaxonomyWhere,
+ Nullable,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type ThematicsCountResponse = {
+ thematics: Nullable<{
+ pageInfo: Pick<GraphQLPageInfo, 'total'>;
+ }>;
+};
+
+const thematicsCountQuery = `query ThematicsCount($search: String, $title: String) {
+ thematics(where: {search: $search, title: $title}) {
+ pageInfo {
+ total
+ }
+ }
+}`;
+
+/**
+ * Retrieve the total of WordPress thematics.
+ *
+ * @param {GraphQLTaxonomyWhere} [input] - The input to filter the thematics.
+ * @returns {Promise<number>} The total number of thematics.
+ */
+export const fetchThematicsCount = async (
+ input?: GraphQLTaxonomyWhere
+): Promise<number> => {
+ const response = await fetchGraphQL<ThematicsCountResponse>({
+ query: thematicsCountQuery,
+ url: getGraphQLUrl(),
+ variables: { ...input },
+ });
+
+ if (!response.thematics)
+ return Promise.reject(
+ new Error('Unable to find the total number of thematics.')
+ );
+
+ return response.thematics.pageInfo.total;
+};
diff --git a/src/services/graphql/fetchers/thematics/fetch-thematics-list.ts b/src/services/graphql/fetchers/thematics/fetch-thematics-list.ts
new file mode 100644
index 0000000..f4d22c6
--- /dev/null
+++ b/src/services/graphql/fetchers/thematics/fetch-thematics-list.ts
@@ -0,0 +1,78 @@
+import type {
+ GraphQLConnection,
+ GraphQLEdgesInput,
+ GraphQLTaxonomyOrderBy,
+ GraphQLTaxonomyWhere,
+ Nullable,
+ WPThematicPreview,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type ThematicsListResponse = {
+ thematics: Nullable<GraphQLConnection<WPThematicPreview>>;
+};
+
+const thematicsListQuery = `query ThematicsList($after: String, $before: String, $first: Int, $last: Int, $orderby: [PostObjectsConnectionOrderbyInput], $search: String, $title: String) {
+ thematics(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: {orderby: $orderby, search: $search, title: $title}
+ ) {
+ edges {
+ cursor
+ node {
+ contentParts {
+ beforeMore
+ }
+ databaseId
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ slug
+ title
+ }
+ }
+ }
+}`;
+
+export type FetchThematicsListInput = GraphQLEdgesInput & {
+ orderBy?: GraphQLTaxonomyOrderBy;
+ where?: GraphQLTaxonomyWhere;
+};
+
+/**
+ * Retrieve a paginated list of WordPress thematics.
+ *
+ * @param {FetchThematicsListInput} input - The input to retrieve thematics.
+ * @returns {Promise<GraphQLConnection<WPThematicPreview>>} The paginated thematics.
+ */
+export const fetchThematicsList = async ({
+ orderBy,
+ where,
+ ...vars
+}: FetchThematicsListInput): Promise<GraphQLConnection<WPThematicPreview>> => {
+ const response = await fetchGraphQL<ThematicsListResponse>({
+ query: thematicsListQuery,
+ url: getGraphQLUrl(),
+ variables: {
+ ...vars,
+ ...where,
+ orderBy: orderBy ? [orderBy] : undefined,
+ },
+ });
+
+ if (!response.thematics)
+ return Promise.reject(new Error('No thematics found.'));
+
+ return response.thematics;
+};
diff --git a/src/services/graphql/fetchers/thematics/index.ts b/src/services/graphql/fetchers/thematics/index.ts
new file mode 100644
index 0000000..c002793
--- /dev/null
+++ b/src/services/graphql/fetchers/thematics/index.ts
@@ -0,0 +1,4 @@
+export * from './fetch-all-thematics-slugs';
+export * from './fetch-thematic';
+export * from './fetch-thematics-count';
+export * from './fetch-thematics-list';
diff --git a/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts b/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts
new file mode 100644
index 0000000..eab4a7c
--- /dev/null
+++ b/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts
@@ -0,0 +1,34 @@
+import type { GraphQLNodes, Nullable, SlugNode } from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+import { fetchTopicsCount } from './fetch-topics-count';
+
+type TopicsSlugsResponse = {
+ topics: Nullable<GraphQLNodes<SlugNode>>;
+};
+
+const topicsSlugsQuery = `query TopicsSlugs($first: Int) {
+ topics(first: $first) {
+ nodes {
+ slug
+ }
+ }
+}`;
+
+/**
+ * Retrieve the WordPress topics slugs.
+ *
+ * @returns {Promise<string[]>} The topics slugs.
+ */
+export const fetchAllTopicsSlugs = async (): Promise<string[]> => {
+ const topicsCount = await fetchTopicsCount();
+ const response = await fetchGraphQL<TopicsSlugsResponse>({
+ query: topicsSlugsQuery,
+ url: getGraphQLUrl(),
+ variables: { first: topicsCount },
+ });
+
+ if (!response.topics)
+ return Promise.reject(new Error('Unable to find the topics slugs.'));
+
+ return response.topics.nodes.map((node) => node.slug);
+};
diff --git a/src/services/graphql/fetchers/topics/fetch-topic.ts b/src/services/graphql/fetchers/topics/fetch-topic.ts
new file mode 100644
index 0000000..efc1d9e
--- /dev/null
+++ b/src/services/graphql/fetchers/topics/fetch-topic.ts
@@ -0,0 +1,97 @@
+import type { Nullable, WPTopic } from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type TopicResponse = {
+ topic: Nullable<WPTopic>;
+};
+
+const topicQuery = `query Topic($slug: ID!) {
+ topic(id: $slug, idType: SLUG) {
+ acfTopics {
+ officialWebsite
+ postsInTopic {
+ ... on Post {
+ acfPosts {
+ postsInThematic {
+ ... on Thematic {
+ databaseId
+ slug
+ title
+ }
+ }
+ }
+ author {
+ node {
+ name
+ }
+ }
+ commentCount
+ contentParts {
+ beforeMore
+ }
+ databaseId
+ date
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ info {
+ wordsCount
+ }
+ modified
+ slug
+ title
+ }
+ }
+ }
+ contentParts {
+ afterMore
+ beforeMore
+ }
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ sourceUrl
+ title
+ }
+ }
+ seo {
+ metaDesc
+ title
+ }
+ slug
+ title
+ }
+}`;
+
+/**
+ * Retrieve a WordPress topic by slug.
+ *
+ * @param {string} slug - The topic slug.
+ * @returns {Promise<WPTopic>} The requested topic.
+ */
+export const fetchTopic = async (slug: string): Promise<WPTopic> => {
+ const response = await fetchGraphQL<TopicResponse>({
+ query: topicQuery,
+ url: getGraphQLUrl(),
+ variables: { slug },
+ });
+
+ if (!response.topic)
+ return Promise.reject(
+ new Error(`No topic found for the following slug ${slug}.`)
+ );
+
+ return response.topic;
+};
diff --git a/src/services/graphql/fetchers/topics/fetch-topics-count.ts b/src/services/graphql/fetchers/topics/fetch-topics-count.ts
new file mode 100644
index 0000000..868b01e
--- /dev/null
+++ b/src/services/graphql/fetchers/topics/fetch-topics-count.ts
@@ -0,0 +1,43 @@
+import type {
+ GraphQLPageInfo,
+ GraphQLTaxonomyWhere,
+ Nullable,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type TopicsCountResponse = {
+ topics: Nullable<{
+ pageInfo: Pick<GraphQLPageInfo, 'total'>;
+ }>;
+};
+
+const topicsCountQuery = `query TopicsCount($search: String, $title: String) {
+ topics(where: {search: $search, title: $title}) {
+ pageInfo {
+ total
+ }
+ }
+}`;
+
+/**
+ * Retrieve the total of WordPress topics.
+ *
+ * @param {GraphQLTaxonomyWhere} [input] - The input to filter the topics.
+ * @returns {Promise<number>} The total number of topics.
+ */
+export const fetchTopicsCount = async (
+ input?: GraphQLTaxonomyWhere
+): Promise<number> => {
+ const response = await fetchGraphQL<TopicsCountResponse>({
+ query: topicsCountQuery,
+ url: getGraphQLUrl(),
+ variables: { ...input },
+ });
+
+ if (!response.topics)
+ return Promise.reject(
+ new Error('Unable to find the total number of topics.')
+ );
+
+ return response.topics.pageInfo.total;
+};
diff --git a/src/services/graphql/fetchers/topics/fetch-topics-list.ts b/src/services/graphql/fetchers/topics/fetch-topics-list.ts
new file mode 100644
index 0000000..1bc2e38
--- /dev/null
+++ b/src/services/graphql/fetchers/topics/fetch-topics-list.ts
@@ -0,0 +1,84 @@
+import type {
+ GraphQLConnection,
+ GraphQLEdgesInput,
+ GraphQLTaxonomyOrderBy,
+ GraphQLTaxonomyWhere,
+ Nullable,
+ WPTopicPreview,
+} from '../../../../types';
+import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers';
+
+type TopicsListResponse = {
+ topics: Nullable<GraphQLConnection<WPTopicPreview>>;
+};
+
+const topicsListQuery = `query TopicsList($after: String, $before: String, $first: Int, $last: Int, $orderby: [PostObjectsConnectionOrderbyInput], $search: String, $title: String) {
+ topics(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: {orderby: $orderby, search: $search, title: $title}
+ ) {
+ edges {
+ cursor
+ node {
+ contentParts {
+ beforeMore
+ }
+ databaseId
+ featuredImage {
+ node {
+ altText
+ mediaDetails {
+ height
+ width
+ }
+ slug
+ title
+ }
+ }
+ slug
+ title
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ total
+ }
+ }
+}`;
+
+export type FetchTopicsListInput = GraphQLEdgesInput & {
+ orderBy?: GraphQLTaxonomyOrderBy;
+ where?: GraphQLTaxonomyWhere;
+};
+
+/**
+ * Retrieve a paginated list of WordPress topics.
+ *
+ * @param {FetchTopicsListInput} input - The input to retrieve topics.
+ * @returns {Promise<GraphQLConnection<WPTopicPreview>>} The paginated topics.
+ */
+export const fetchTopicsList = async ({
+ orderBy,
+ where,
+ ...vars
+}: FetchTopicsListInput): Promise<GraphQLConnection<WPTopicPreview>> => {
+ const response = await fetchGraphQL<TopicsListResponse>({
+ query: topicsListQuery,
+ url: getGraphQLUrl(),
+ variables: {
+ ...vars,
+ ...where,
+ orderBy: orderBy ? [orderBy] : undefined,
+ },
+ });
+
+ if (!response.topics) return Promise.reject(new Error('No topics found.'));
+
+ return response.topics;
+};
diff --git a/src/services/graphql/fetchers/topics/index.ts b/src/services/graphql/fetchers/topics/index.ts
new file mode 100644
index 0000000..e381883
--- /dev/null
+++ b/src/services/graphql/fetchers/topics/index.ts
@@ -0,0 +1,4 @@
+export * from './fetch-all-topics-slugs';
+export * from './fetch-topic';
+export * from './fetch-topics-count';
+export * from './fetch-topics-list';