aboutsummaryrefslogtreecommitdiffstats
path: root/src/services/graphql/api.ts
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-05-24 19:35:12 +0200
committerGitHub <noreply@github.com>2022-05-24 19:35:12 +0200
commitc85ab5ad43ccf52881ee224672c41ec30021cf48 (patch)
tree8058808d9bfca19383f120c46b34d99ff2f89f63 /src/services/graphql/api.ts
parent52404177c07a2aab7fc894362fb3060dff2431a0 (diff)
parent11b9de44a4b2f305a6a484187805e429b2767118 (diff)
refactor: use storybook and atomic design (#16)
BREAKING CHANGE: rewrite most of the Typescript types, so the content format (the meta in particular) needs to be updated.
Diffstat (limited to 'src/services/graphql/api.ts')
-rw-r--r--src/services/graphql/api.ts319
1 files changed, 303 insertions, 16 deletions
diff --git a/src/services/graphql/api.ts b/src/services/graphql/api.ts
index a5be026..009aea4 100644
--- a/src/services/graphql/api.ts
+++ b/src/services/graphql/api.ts
@@ -1,25 +1,312 @@
-import { RequestType, VariablesType } from '@ts/types/app';
import { settings } from '@utils/config';
-import { GraphQLClient } from 'graphql-request';
+import {
+ articleBySlugQuery,
+ articlesCardQuery,
+ articlesEndCursor,
+ articlesQuery,
+ articlesSlugQuery,
+ totalArticlesQuery,
+} from './articles.query';
+import { sendCommentMutation } from './comments.mutation';
+import { commentsQuery } from './comments.query';
+import { sendMailMutation } from './contact.mutation';
+import {
+ thematicBySlugQuery,
+ thematicsListQuery,
+ thematicsSlugQuery,
+ totalThematicsQuery,
+} from './thematics.query';
+import {
+ topicBySlugQuery,
+ topicsListQuery,
+ topicsSlugQuery,
+ totalTopicsQuery,
+} from './topics.query';
-export const getGraphQLClient = (): GraphQLClient => {
- const apiUrl = settings.api.url;
+export type Mutations = typeof sendMailMutation | typeof sendCommentMutation;
- if (!apiUrl) throw new Error('API URL not defined.');
+export type Queries =
+ | typeof articlesQuery
+ | typeof articleBySlugQuery
+ | typeof articlesCardQuery
+ | typeof articlesEndCursor
+ | typeof articlesSlugQuery
+ | typeof commentsQuery
+ | typeof thematicBySlugQuery
+ | typeof thematicsListQuery
+ | typeof thematicsSlugQuery
+ | typeof topicBySlugQuery
+ | typeof topicsListQuery
+ | typeof topicsSlugQuery
+ | typeof totalArticlesQuery
+ | typeof totalThematicsQuery
+ | typeof totalTopicsQuery;
- return new GraphQLClient(apiUrl);
+export type ArticleResponse<T> = {
+ post: T;
};
-export const fetchApi = async <T extends RequestType>(
- query: string,
- variables: VariablesType<T>
-): Promise<T> => {
- const client = getGraphQLClient();
+export type ArticlesResponse<T> = {
+ posts: T;
+};
+
+export type CommentsResponse<T> = {
+ comments: T;
+};
+
+export type SendCommentResponse<T> = {
+ createComment: T;
+};
+
+export type SendMailResponse<T> = {
+ sendEmail: T;
+};
- try {
- return await client.request(query, variables);
- } catch (error) {
- console.error(error, undefined, 2);
- process.exit(1);
+export type ThematicResponse<T> = {
+ thematic: T;
+};
+
+export type ThematicsResponse<T> = {
+ thematics: T;
+};
+
+export type TopicResponse<T> = {
+ topic: T;
+};
+
+export type TopicsResponse<T> = {
+ topics: T;
+};
+
+export type PageInfo = {
+ endCursor: string;
+ hasNextPage: boolean;
+ total: number;
+};
+
+export type Edges<T> = {
+ cursor: string;
+ node: T;
+};
+
+export type EdgesResponse<T> = {
+ edges: Edges<T>[];
+ pageInfo: PageInfo;
+};
+
+export type NodeResponse<T> = {
+ node: T;
+};
+
+export type NodesResponse<T> = {
+ nodes: T[];
+};
+
+export type EndCursor = Pick<
+ EdgesResponse<Pick<PageInfo, 'endCursor'>>,
+ 'pageInfo'
+>;
+
+export type ResponseMap<T> = {
+ [articleBySlugQuery]: ArticleResponse<T>;
+ [articlesCardQuery]: ArticlesResponse<NodesResponse<T>>;
+ [articlesEndCursor]: ArticlesResponse<EndCursor>;
+ [articlesQuery]: ArticlesResponse<EdgesResponse<T>>;
+ [articlesSlugQuery]: ArticlesResponse<EdgesResponse<T>>;
+ [commentsQuery]: CommentsResponse<NodesResponse<T>>;
+ [sendCommentMutation]: SendCommentResponse<T>;
+ [sendMailMutation]: SendMailResponse<T>;
+ [thematicBySlugQuery]: ThematicResponse<T>;
+ [thematicsListQuery]: ThematicsResponse<EdgesResponse<T>>;
+ [thematicsSlugQuery]: ThematicsResponse<EdgesResponse<T>>;
+ [topicBySlugQuery]: TopicResponse<T>;
+ [topicsListQuery]: TopicsResponse<EdgesResponse<T>>;
+ [topicsSlugQuery]: TopicsResponse<EdgesResponse<T>>;
+ [totalArticlesQuery]: ArticlesResponse<T>;
+ [totalThematicsQuery]: ThematicsResponse<T>;
+ [totalTopicsQuery]: TopicsResponse<T>;
+};
+
+export type GraphQLResponse<
+ T extends keyof ResponseMap<U>,
+ U
+> = ResponseMap<U>[T];
+
+export type BySlugVar = {
+ /**
+ * A slug.
+ */
+ slug: string;
+};
+
+export type EdgesVars = {
+ /**
+ * A cursor.
+ */
+ after?: string;
+ /**
+ * The number of items to return.
+ */
+ first: number;
+ /**
+ * A search query.
+ */
+ search?: string;
+};
+
+export type ByContentIdVar = {
+ /**
+ * An article id.
+ */
+ contentId: number;
+};
+
+export type SearchVar = {
+ /**
+ * A search term.
+ */
+ search?: string;
+};
+
+export type SendCommentVars = {
+ /**
+ * The author name.
+ */
+ author: string;
+ /**
+ * The author e-mail address.
+ */
+ authorEmail: string;
+ /**
+ * The author website.
+ */
+ authorUrl: string;
+ /**
+ * A mutation id.
+ */
+ clientMutationId: string;
+ /**
+ * A post or page id.
+ */
+ commentOn: number;
+ /**
+ * The comment body.
+ */
+ content: string;
+ /**
+ * The comment parent.
+ */
+ parent?: number;
+};
+
+export type SendMailVars = {
+ /**
+ * The mail body.
+ */
+ body: string;
+ /**
+ * A mutation id.
+ */
+ clientMutationId: string;
+ /**
+ * The reply to e-mail address.
+ */
+ replyTo: string;
+ /**
+ * The mail subject.
+ */
+ subject: string;
+};
+
+export type VariablesMap = {
+ [articleBySlugQuery]: BySlugVar;
+ [articlesCardQuery]: EdgesVars;
+ [articlesEndCursor]: EdgesVars;
+ [articlesQuery]: EdgesVars;
+ [articlesSlugQuery]: EdgesVars;
+ [commentsQuery]: ByContentIdVar;
+ [sendCommentMutation]: SendCommentVars;
+ [sendMailMutation]: SendMailVars;
+ [thematicBySlugQuery]: BySlugVar;
+ [thematicsListQuery]: EdgesVars;
+ [thematicsSlugQuery]: EdgesVars;
+ [topicBySlugQuery]: BySlugVar;
+ [topicsListQuery]: EdgesVars;
+ [topicsSlugQuery]: EdgesVars;
+ [totalArticlesQuery]: SearchVar;
+ [totalThematicsQuery]: null;
+ [totalTopicsQuery]: null;
+};
+
+export type FetchAPIProps<T extends Queries | Mutations> = {
+ /**
+ * A GraphQL API URL.
+ */
+ api: string;
+ /**
+ * A GraphQL query.
+ */
+ query: T;
+ /**
+ * (Optional) The query variables.
+ */
+ variables?: VariablesMap[T];
+};
+
+/**
+ * Fetch a GraphQL API.
+ * @param {object} obj - An object.
+ * @param {string} obj.api - A GraphQL API URL.
+ * @param {Queries} obj.query - A GraphQL query.
+ * @param {object} [obj.variables] - The query variables.
+ */
+export async function fetchAPI<T, U extends Queries | Mutations>({
+ api,
+ query,
+ variables,
+}: FetchAPIProps<U>): Promise<GraphQLResponse<U, T>> {
+ const response = await fetch(api, {
+ method: 'POST',
+ headers: {
+ 'content-type': 'application/json;charset=UTF-8',
+ },
+ body: JSON.stringify({
+ query,
+ variables,
+ }),
+ });
+
+ type JSONResponse = {
+ data?: GraphQLResponse<U, T>;
+ errors?: Array<{ message: string }>;
+ };
+
+ const { data, errors }: JSONResponse = await response.json();
+
+ if (response.ok) {
+ if (!data) return Promise.reject(new Error(`No data found"`));
+
+ return data;
+ } else {
+ console.error('Failed to fetch API');
+ const error = new Error(
+ errors?.map((e) => e.message).join('\n') ?? 'unknown'
+ );
+ return Promise.reject(error);
+ }
+}
+
+/**
+ * Retrieve the API url from settings.
+ *
+ * @returns {string} The API url.
+ */
+export const getAPIUrl = (): string => {
+ const { url } = settings.api;
+
+ if (!url) {
+ throw new Error('API url is not defined.');
}
+
+ return url;
};