Skip to content

Instantly share code, notes, and snippets.

@SagnikPradhan
Created May 20, 2024 15:26
Show Gist options
  • Save SagnikPradhan/d2a520cb26863dd38dbd8c8a7761ba06 to your computer and use it in GitHub Desktop.
Save SagnikPradhan/d2a520cb26863dd38dbd8c8a7761ba06 to your computer and use it in GitHub Desktop.
GraphQL Client
import { TypedDocumentString } from "~/graphql/graphql";
import extractFiles, { ExtractableFile } from "extract-files/extractFiles.mjs";
import isExtractableFile from "extract-files/isExtractableFile.mjs";
import { ExecutionResult } from "graphql";
type BaseFetcherProps<GQLResult, GQLVariables> = {
document: TypedDocumentString<GQLResult, GQLVariables>;
signal?: AbortSignal;
};
type FetcherProps<GQLResult, GQLVariables> = GQLVariables extends Record<
string,
never
>
? BaseFetcherProps<GQLResult, GQLVariables> & { variables?: GQLVariables }
: BaseFetcherProps<GQLResult, GQLVariables> & { variables: GQLVariables };
export async function fetcher<GQLResult, GQLVariables>({
document,
variables = {} as GQLVariables,
signal,
}: FetcherProps<GQLResult, GQLVariables>): Promise<GQLResult | undefined> {
const clonedQuery = extractFiles({ variables }, isExtractableFile);
const endpoint = new URL("/api/graphql", window.location.href);
const request =
clonedQuery.files.size > 0
? await fetch(endpoint, {
method: "POST",
signal,
body: createGQLMultipartBody({
query: document.toString(),
variables: clonedQuery.clone.variables,
filesMap: clonedQuery.files,
}),
})
: await fetch(endpoint, {
method: "POST",
signal,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: document.toString(), variables }),
});
return request.json().then((result: ExecutionResult<GQLResult>) => {
if (result.errors?.length) throw result.errors[0];
else return result.data ?? undefined;
});
}
interface GQLMultipartBodyOptions<V> {
query: string;
variables: V;
filesMap: Map<ExtractableFile, string[]>;
}
function createGQLMultipartBody<V>({
query,
variables,
filesMap,
}: GQLMultipartBodyOptions<V>) {
const body = new FormData();
const files = Array.from(filesMap.entries());
const operations = { query, variables };
const map = Object.fromEntries(files.map(([_, path], idx) => [idx, path]));
body.append("operations", JSON.stringify(operations));
body.append("map", JSON.stringify(map));
files.forEach(([file], idx) => body.append(idx.toString(), file));
return body;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment