From 9308a6dce03bd0c616e0ba6fec227473aaa44b33 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Mon, 2 May 2022 12:55:13 +0200 Subject: refactor: rewrite API fetching method and GraphQL queries --- src/services/graphql/api.ts | 222 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 src/services/graphql/api.ts (limited to 'src/services/graphql/api.ts') diff --git a/src/services/graphql/api.ts b/src/services/graphql/api.ts new file mode 100644 index 0000000..b0e8d3a --- /dev/null +++ b/src/services/graphql/api.ts @@ -0,0 +1,222 @@ +import { settings } from '@utils/config'; +import { + articleBySlugQuery, + articlesCardQuery, + articlesQuery, + articlesSlugQuery, + totalArticlesQuery, +} from './articles.query'; +import { commentsQuery } from './comments.query'; +import { + thematicBySlugQuery, + thematicsListQuery, + thematicsSlugQuery, +} from './thematics.query'; +import { + topicBySlugQuery, + topicsListQuery, + topicsSlugQuery, +} from './topics.query'; + +export type Queries = + | typeof articlesQuery + | typeof articleBySlugQuery + | typeof articlesCardQuery + | typeof articlesSlugQuery + | typeof commentsQuery + | typeof thematicBySlugQuery + | typeof thematicsListQuery + | typeof thematicsSlugQuery + | typeof topicBySlugQuery + | typeof topicsListQuery + | typeof topicsSlugQuery + | typeof totalArticlesQuery; + +export type ArticleResponse = { + post: T; +}; + +export type ArticlesResponse = { + posts: T; +}; + +export type CommentsResponse = { + comments: T[]; +}; + +export type ThematicResponse = { + thematic: T; +}; + +export type ThematicsResponse = { + thematics: T; +}; + +export type TopicResponse = { + topic: T; +}; + +export type TopicsResponse = { + topics: T; +}; + +export type PageInfo = { + endCursor: string; + hasNextPage: boolean; + total: number; +}; + +export type Edges = { + cursor: string; + node: T; +}; + +export type EdgesResponse = { + edges: Edges[]; + pageInfo: PageInfo; +}; + +export type NodeResponse = { + node: T; +}; + +export type NodesResponse = { + nodes: T[]; +}; + +export type ResponseMap = { + [articleBySlugQuery]: ArticleResponse>; + [articlesCardQuery]: ArticlesResponse>; + [articlesQuery]: ArticlesResponse>; + [articlesSlugQuery]: ArticlesResponse>; + [commentsQuery]: CommentsResponse>; + [thematicBySlugQuery]: ThematicResponse>; + [thematicsListQuery]: ThematicsResponse>; + [thematicsSlugQuery]: ThematicsResponse>; + [topicBySlugQuery]: TopicResponse>; + [topicsListQuery]: TopicsResponse>; + [topicsSlugQuery]: TopicsResponse>; + [totalArticlesQuery]: ArticlesResponse; +}; + +export type GraphQLResponse< + T extends keyof ResponseMap, + U +> = ResponseMap[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 VariablesMap = { + [articleBySlugQuery]: BySlugVar; + [articlesCardQuery]: EdgesVars; + [articlesQuery]: EdgesVars; + [articlesSlugQuery]: EdgesVars; + [commentsQuery]: ByContentIdVar; + [thematicBySlugQuery]: BySlugVar; + [thematicsListQuery]: EdgesVars; + [thematicsSlugQuery]: EdgesVars; + [topicBySlugQuery]: BySlugVar; + [topicsListQuery]: EdgesVars; + [topicsSlugQuery]: EdgesVars; + [totalArticlesQuery]: null; +}; + +export type FetchAPIProps = { + /** + * 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({ + api, + query, + variables, +}: FetchAPIProps): Promise> { + const response = await fetch(api, { + method: 'POST', + headers: { + 'content-type': 'application/json;charset=UTF-8', + }, + body: JSON.stringify({ + query, + variables, + }), + }); + + type JSONResponse = { + data?: GraphQLResponse; + 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; +}; -- cgit v1.2.3