Skip to content
Draft

Urql #21

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/components/ArticlesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Link } from "remix";
import type { ArticleItemFragment } from "~/graphql/generated";
import { formatRelative, parseISO } from "date-fns";
import { getDateFnsLocale } from "~/utils/i18n";
import { gql } from "~/utils/dato";
import { gql } from "@urql/core";

export const itemFragment = gql`
fragment articleItem on ArticleRecord {
Expand Down
2 changes: 1 addition & 1 deletion app/components/GNB.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Globe } from "react-feather";
import { Link } from "remix";
import invariant from "tiny-invariant";
import { GnbFragmentFragment } from "~/graphql/generated";
import { gql } from "~/utils/dato";
import { gql } from "@urql/core";

interface GNBProps {
data: GnbFragmentFragment | null;
Expand Down
2 changes: 1 addition & 1 deletion app/components/Image.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import invariant from "tiny-invariant";
import { ImageFragment } from "~/graphql/generated";
import { gql } from "~/utils/dato";
import { gql } from "@urql/core";

export const fragment = gql`
fragment Image on ImageRecord {
Expand Down
8 changes: 6 additions & 2 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { renderToPipeableStream } from "react-dom/server";
import { createCookie, RemixServer } from "remix";
import type { EntryContext } from "remix";
import { gql, load } from "./utils/dato";
import { load } from "./utils/dato";
import { GetLocalesQuery } from "./graphql/generated";
import { PassThrough } from "stream";
import { gql } from "@urql/core";

import "dotenv/config";
import invariant from "tiny-invariant";

let locales: string[];

Expand All @@ -23,7 +25,7 @@ export default async function handleRequest(
});

if (!locales) {
const data: GetLocalesQuery = await load({
const { data, error } = await load<GetLocalesQuery>({
query: gql`
query getLocales {
_site {
Expand All @@ -32,6 +34,8 @@ export default async function handleRequest(
}
`,
});
if (error) throw error;
invariant(data, "data is undefined");
locales = data._site.locales;
}

Expand Down
3 changes: 2 additions & 1 deletion app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import css from "./theme.css";
import logo from "~/assets/basixlab.svg";

import GNB, { fragment as gnbFragment } from "./components/GNB";
import { datoQuerySubscription, gql, QueryListenerOptions } from "./utils/dato";
import { datoQuerySubscription, QueryListenerOptions } from "./utils/dato";
import { gql } from "@urql/core";
import { RootQuery } from "./graphql/generated";
import { renderMetaTags, useQuerySubscription } from "react-datocms";
import { MetaTagsFragment } from "./graphql/fragments";
Expand Down
3 changes: 2 additions & 1 deletion app/routes/$locale/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import ArticlesList from "~/components/ArticlesList";
import { StructuredText } from "~/components/dato";
import { HomepageQuery } from "~/graphql/generated";
import { OutletData } from "~/root";
import { datoQuerySubscription, gql, QueryListenerOptions } from "~/utils/dato";
import { datoQuerySubscription, QueryListenerOptions } from "~/utils/dato";
import { gql } from "@urql/core";
import { fragment as articlesListFragment } from "~/components/ArticlesList";
import { fragment as imageFragment } from "~/components/Image";

Expand Down
8 changes: 3 additions & 5 deletions app/routes/$locale/posts/$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
useOutletContext,
} from "remix";
import { Giscus } from "@giscus/react";
import { datoQuerySubscription, gql, QueryListenerOptions } from "~/utils/dato";
import { datoQuerySubscription, QueryListenerOptions } from "~/utils/dato";
import { gql } from "@urql/core";
import type { ArticlesListRecord, GetPostQuery } from "~/graphql/generated";
import { toRemixMeta, useQuerySubscription } from "react-datocms";
import { MetaTagsFragment } from "~/graphql/fragments";
Expand Down Expand Up @@ -83,10 +84,7 @@ export default function Post() {
<h3>{data?.article?.description}</h3>
</hgroup>
</header>
<StructuredText
data={data?.article?.content}
locale={locale}
/>
<StructuredText data={data?.article?.content} locale={locale} />
</article>
{data?.article?.comments && (
<Giscus
Expand Down
3 changes: 2 additions & 1 deletion app/routes/$locale/posts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import invariant from "tiny-invariant";
import ArticlesList, { itemFragment as articleFragment } from "~/components/ArticlesList";
import { GetPostsQuery } from "~/graphql/generated";
import { OutletData } from "~/root";
import { datoQuerySubscription, gql, QueryListenerOptions } from "~/utils/dato";
import { datoQuerySubscription, QueryListenerOptions } from "~/utils/dato";
import { gql } from "@urql/core";

export const loader = async ({
params,
Expand Down
76 changes: 39 additions & 37 deletions app/utils/dato.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,81 @@
import dedent from "dedent";
import { OperationResult, TypedDocumentNode } from "@urql/core";
import { DocumentNode, print } from "graphql";
import type { QueryListenerOptions as DatoQueryListenerOptions } from "react-datocms";
import { defaultClient, ENDPOINT, getClient } from "./graphql.server";
import { getSession } from "./sessions.server";

interface LoadOptions {
query: string;
variables?: { [key: string]: any };
interface LoadOptions<T, V extends object = Record<string, any>> {
query: string | DocumentNode | TypedDocumentNode<T, V>;
variables?: V;
preview?: boolean;
}

export async function load<T>({
export async function load<T, V extends object = Record<string, any>>({
query,
variables,
preview,
}: LoadOptions): Promise<T> {
let endpoint = "https://graphql.datocms.com";
}: LoadOptions<T, V>): Promise<OperationResult<T, {}>> {
let endpoint = ENDPOINT;

if (process.env.DATOCMS_ENVIRONMENT)
endpoint += `/environments/${process.env.DATOCMS_ENVIRONMENT}`;
if (preview) endpoint += `/preview`;

const headers = new Headers({
Authorization: `Bearer ${process.env.DATOCMS_READONLY_TOKEN}`,
});

const res = await fetch(endpoint, {
method: "POST",
headers,
body: JSON.stringify({
query,
variables,
}),
});
const client = endpoint === ENDPOINT ? defaultClient : getClient(endpoint);

const json = await res.json();

if (!res.ok || json.errors) {
console.error("Ouch! The query has some errors!", JSON.stringify(json.errors, null, 2));
throw new Error(json.errors ?? json);
}

return json.data;
return await client.query<T, V>(query, variables).toPromise();
}

function getEnvironment() {
if (process.env.DATOCMS_ENVIRONMENT) return process.env.DATOCMS_ENVIRONMENT;
else if (process.env.NODE_ENV === "development") return "development";
else if (process.env.VERCEL_GIT_COMMIT_REF?.startsWith("env/")) return process.env.VERCEL_GIT_COMMIT_REF.slice(4);
else if (process.env.VERCEL_GIT_COMMIT_REF?.startsWith("env/"))
return process.env.VERCEL_GIT_COMMIT_REF.slice(4);
}

interface QueryOptions extends LoadOptions {
interface QueryOptions<T, V extends object> extends LoadOptions<T, V> {
request: Request;
}

export async function datoQuerySubscription<T = any>({
export async function datoQuerySubscription<
T = any,
V extends object = Record<string, any>
>({
request,
query,
...gqlRequest
}: QueryOptions): Promise<DatoQueryListenerOptions<T, Record<string, any>>> {
}: QueryOptions<T, V>): Promise<
DatoQueryListenerOptions<T, V>
> {
const session = await getSession(request.headers.get("Cookie"));
const previewEnabled = session.get("preview");

const { data, error } = await load<T>({
...gqlRequest,
query,
preview: Boolean(previewEnabled),
});

const stringQuery = typeof query !== 'string' ? print(query) : query;

if (error) throw error;

return previewEnabled
? {
...gqlRequest,
query: stringQuery,
preview: true,
initialData: await load<T>({ ...gqlRequest, preview: true }),
initialData: data,
token: process.env.DATOCMS_READONLY_TOKEN!,
environment: getEnvironment(),
}
: {
enabled: false,
initialData: await load<T>(gqlRequest),
initialData: data,
};
}

export type QueryListenerOptions<T> = DatoQueryListenerOptions<T, Record<string, any>>;

// Trick for better editor support.
export const gql = dedent;
export type QueryListenerOptions<T> = DatoQueryListenerOptions<
T,
Record<string, any>
>;
3 changes: 2 additions & 1 deletion app/utils/feed.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Feed } from "feed";
import { FeedQuery } from "~/graphql/generated";
import { gql, load } from "./dato";
import { load } from "./dato";
import { gql } from "@urql/core";
import { render } from "datocms-structured-text-to-plain-text";

export async function getFeed(url: URL, language: string): Promise<Feed> {
Expand Down
23 changes: 23 additions & 0 deletions app/utils/graphql.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createClient, ClientOptions } from "@urql/core";

const clientOptions: Omit<ClientOptions, "url"> = {
fetchOptions: {
headers: {
Authorization: `Bearer ${process.env.DATOCMS_READONLY_TOKEN}`,
},
},
};

export const ENDPOINT = "https://graphql.datocms.com";

export const defaultClient = createClient({
url: ENDPOINT,
...clientOptions,
});

export function getClient(url: string) {
return createClient({
url,
...clientOptions,
});
}
2 changes: 1 addition & 1 deletion codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ generates:
- typescript-document-nodes
config:
importOperationTypesFrom: ./generated
gqlImport: ~/utils/dato#gql
gqlImport: "@urql/core#gql"
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"@remix-run/react": "^1.1.3",
"@remix-run/serve": "^1.1.3",
"@remix-run/vercel": "patch:@remix-run/vercel@npm:1.1.3#.yarn/patches/@remix-run-vercel-npm-1.1.3-3d4b846e7d",
"@urql/core": "^2.4.1",
"@urql/exchange-graphcache": "^4.3.6",
"date-fns": "^2.28.0",
"datocms-structured-text-to-plain-text": "^2.0.0",
"dedent": "^0.7.0",
Expand All @@ -29,7 +31,8 @@
"remix": "^1.1.3",
"remix-crash": "^0.1.2",
"rosetta": "^1.1.0",
"tiny-invariant": "^1.2.0"
"tiny-invariant": "^1.2.0",
"urql": "^2.1.3"
},
"devDependencies": {
"@graphql-codegen/cli": "^2.5.0",
Expand Down
Loading