Simplify GraphQL setup

This removes the unified GraphQL cache, as invalidation is annoying.
It also fixes inconsistent codegen behaviors.
This commit is contained in:
Quentin Gliech
2024-10-22 14:55:33 +02:00
parent 1af96c710e
commit 9bde01df5b
9 changed files with 36 additions and 5195 deletions
+1 -11
View File
@@ -6,13 +6,6 @@
import type { CodegenConfig } from "@graphql-codegen/cli";
// Adds a comment to the top of generated files to ignore linting and formatting
const lintIgnore = {
add: {
content: "/* prettier-ignore */\n/* eslint-disable */",
},
} as const;
const config: CodegenConfig = {
schema: "./schema.graphql",
documents: ["src/**/*.{tsx,ts}", "!src/gql/**/*"],
@@ -21,6 +14,7 @@ const config: CodegenConfig = {
"./src/gql/": {
preset: "client",
config: {
useTypeImports: true,
// By default, unknown scalars are generated as `any`. This is not ideal for catching potential bugs.
defaultScalarType: "unknown",
scalars: {
@@ -28,10 +22,6 @@ const config: CodegenConfig = {
Url: "string",
},
},
plugins: [lintIgnore],
},
"./src/gql/schema.ts": {
plugins: ["urql-introspection", lintIgnore],
},
},
};
+18 -97
View File
@@ -16,7 +16,6 @@
"@tanstack/router-zod-adapter": "^1.70.0",
"@urql/core": "^5.0.6",
"@urql/devtools": "^2.0.3",
"@urql/exchange-graphcache": "^7.2.0",
"@urql/exchange-refocus": "^1.1.0",
"@urql/exchange-request-policy": "^1.2.0",
"@vector-im/compound-design-tokens": "1.8.0",
@@ -43,7 +42,7 @@
"@codecov/vite-plugin": "^1.2.0",
"@graphql-codegen/add": "^5.0.3",
"@graphql-codegen/cli": "^5.0.3",
"@graphql-codegen/client-preset": "4.3.3",
"@graphql-codegen/client-preset": "^4.4.0",
"@graphql-codegen/urql-introspection": "^3.0.0",
"@storybook/addon-essentials": "^8.3.5",
"@storybook/react": "^8.3.5",
@@ -2181,54 +2180,6 @@
}
}
},
"node_modules/@graphql-codegen/cli/node_modules/@graphql-codegen/client-preset": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.4.0.tgz",
"integrity": "sha512-Q0NHFK7KXLhEaRC/k82ge0dHDfeHJEvvDeV0vV3+oSurHNa/lpxQtbK2BqknZe+JDfZ1YOOvYT93XsAkYD+SQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/template": "^7.20.7",
"@graphql-codegen/add": "^5.0.3",
"@graphql-codegen/gql-tag-operations": "4.0.10",
"@graphql-codegen/plugin-helpers": "^5.0.4",
"@graphql-codegen/typed-document-node": "^5.0.10",
"@graphql-codegen/typescript": "^4.1.0",
"@graphql-codegen/typescript-operations": "^4.3.0",
"@graphql-codegen/visitor-plugin-common": "^5.4.0",
"@graphql-tools/documents": "^1.0.0",
"@graphql-tools/utils": "^10.0.0",
"@graphql-typed-document-node/core": "3.2.0",
"tslib": "~2.6.0"
},
"engines": {
"node": ">=16"
},
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/@graphql-codegen/cli/node_modules/@graphql-codegen/gql-tag-operations": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.10.tgz",
"integrity": "sha512-WsBEVL3XQdBboFJJL5WxrUjkuo3B7Sa51R9NbT7PKBe0HCNstoouGZIvQJRUubttFCqTTyoFtNsoRSKB+rsRug==",
"dev": true,
"license": "MIT",
"dependencies": {
"@graphql-codegen/plugin-helpers": "^5.0.4",
"@graphql-codegen/visitor-plugin-common": "5.4.0",
"@graphql-tools/utils": "^10.0.0",
"auto-bind": "~4.0.0",
"tslib": "~2.6.0"
},
"engines": {
"node": ">=16"
},
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/@graphql-codegen/cli/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -2286,26 +2237,29 @@
}
},
"node_modules/@graphql-codegen/client-preset": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.3.3.tgz",
"integrity": "sha512-IrDsSVe8bkKtxgVfKPHzjL9tYlv7KEpA59R4gZLqx/t2WIJncW1i0OMvoz9tgoZsFEs8OKKgXZbnwPZ/Qf1kEw==",
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.4.0.tgz",
"integrity": "sha512-Q0NHFK7KXLhEaRC/k82ge0dHDfeHJEvvDeV0vV3+oSurHNa/lpxQtbK2BqknZe+JDfZ1YOOvYT93XsAkYD+SQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/template": "^7.20.7",
"@graphql-codegen/add": "^5.0.3",
"@graphql-codegen/gql-tag-operations": "4.0.9",
"@graphql-codegen/gql-tag-operations": "4.0.10",
"@graphql-codegen/plugin-helpers": "^5.0.4",
"@graphql-codegen/typed-document-node": "^5.0.9",
"@graphql-codegen/typescript": "^4.0.9",
"@graphql-codegen/typescript-operations": "^4.2.3",
"@graphql-codegen/visitor-plugin-common": "^5.3.1",
"@graphql-codegen/typed-document-node": "^5.0.10",
"@graphql-codegen/typescript": "^4.1.0",
"@graphql-codegen/typescript-operations": "^4.3.0",
"@graphql-codegen/visitor-plugin-common": "^5.4.0",
"@graphql-tools/documents": "^1.0.0",
"@graphql-tools/utils": "^10.0.0",
"@graphql-typed-document-node/core": "3.2.0",
"tslib": "~2.6.0"
},
"engines": {
"node": ">=16"
},
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
@@ -2327,39 +2281,20 @@
}
},
"node_modules/@graphql-codegen/gql-tag-operations": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.9.tgz",
"integrity": "sha512-lVgu1HClel896HqZAEjynatlU6eJrYOw+rh05DPgM150xvmb7Gz5TnRHA2vfwlDNIXDaToAIpz5RFfkjjnYM1Q==",
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.10.tgz",
"integrity": "sha512-WsBEVL3XQdBboFJJL5WxrUjkuo3B7Sa51R9NbT7PKBe0HCNstoouGZIvQJRUubttFCqTTyoFtNsoRSKB+rsRug==",
"dev": true,
"license": "MIT",
"dependencies": {
"@graphql-codegen/plugin-helpers": "^5.0.4",
"@graphql-codegen/visitor-plugin-common": "5.3.1",
"@graphql-codegen/visitor-plugin-common": "5.4.0",
"@graphql-tools/utils": "^10.0.0",
"auto-bind": "~4.0.0",
"tslib": "~2.6.0"
},
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/@graphql-codegen/gql-tag-operations/node_modules/@graphql-codegen/visitor-plugin-common": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.3.1.tgz",
"integrity": "sha512-MktoBdNZhSmugiDjmFl1z6rEUUaqyxtFJYWnDilE7onkPgyw//O0M+TuPBJPBWdyV6J2ond0Hdqtq+rkghgSIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@graphql-codegen/plugin-helpers": "^5.0.4",
"@graphql-tools/optimize": "^2.0.0",
"@graphql-tools/relay-operation-optimizer": "^7.0.0",
"@graphql-tools/utils": "^10.0.0",
"auto-bind": "~4.0.0",
"change-case-all": "1.0.15",
"dependency-graph": "^0.11.0",
"graphql-tag": "^2.11.0",
"parse-filepath": "^1.0.2",
"tslib": "~2.6.0"
"engines": {
"node": ">=16"
},
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
@@ -6332,20 +6267,6 @@
"graphql": ">= 0.11.0"
}
},
"node_modules/@urql/exchange-graphcache": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@urql/exchange-graphcache/-/exchange-graphcache-7.2.0.tgz",
"integrity": "sha512-NPSfcHyhZnqTuu2OtBTS0b80YMQe1fOEpECZWyRd1A4WSfU/BJjsNSI/AXnG7ABGGCMtLvfTWRSlzsoLwPjwlQ==",
"license": "MIT",
"dependencies": {
"@0no-co/graphql.web": "^1.0.5",
"@urql/core": "^5.0.0",
"wonka": "^6.3.2"
},
"peerDependencies": {
"@urql/core": "^5.0.0"
}
},
"node_modules/@urql/exchange-refocus": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@urql/exchange-refocus/-/exchange-refocus-1.1.0.tgz",
+1 -2
View File
@@ -25,7 +25,6 @@
"@tanstack/router-zod-adapter": "^1.70.0",
"@urql/core": "^5.0.6",
"@urql/devtools": "^2.0.3",
"@urql/exchange-graphcache": "^7.2.0",
"@urql/exchange-refocus": "^1.1.0",
"@urql/exchange-request-policy": "^1.2.0",
"@vector-im/compound-design-tokens": "1.8.0",
@@ -52,7 +51,7 @@
"@codecov/vite-plugin": "^1.2.0",
"@graphql-codegen/add": "^5.0.3",
"@graphql-codegen/cli": "^5.0.3",
"@graphql-codegen/client-preset": "4.3.3",
"@graphql-codegen/client-preset": "^4.4.0",
"@graphql-codegen/urql-introspection": "^3.0.0",
"@storybook/addon-essentials": "^8.3.5",
"@storybook/react": "^8.3.5",
+3 -3
View File
@@ -1,7 +1,7 @@
/* eslint-disable */
import { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import { FragmentDefinitionNode } from 'graphql';
import { Incremental } from './graphql';
import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import type { FragmentDefinitionNode } from 'graphql';
import type { Incremental } from './graphql';
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
+2 -1
View File
@@ -1,6 +1,6 @@
/* eslint-disable */
import * as types from './graphql';
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
/**
* Map of all GraphQL operations in the project.
@@ -11,6 +11,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
* 3. It does not support dead code elimination, so it will add unused operations.
*
* Therefore it is highly recommended to use the babel or swc plugin for production.
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
*/
const documents = {
"\n fragment PasswordChange_siteConfig on SiteConfig {\n id\n passwordChangeAllowed\n }\n": types.PasswordChange_SiteConfigFragmentDoc,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -97
View File
@@ -4,106 +4,12 @@
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
import { createClient, fetchExchange } from "@urql/core";
import { cacheExchange, createClient, fetchExchange } from "@urql/core";
import { devtoolsExchange } from "@urql/devtools";
import { cacheExchange } from "@urql/exchange-graphcache";
import { refocusExchange } from "@urql/exchange-refocus";
import { requestPolicyExchange } from "@urql/exchange-request-policy";
import appConfig from "./config";
import type {
MutationAddEmailArgs,
MutationRemoveEmailArgs,
MutationVerifyEmailArgs,
RemoveEmailPayload,
VerifyEmailPayload,
} from "./gql/graphql";
import schema from "./gql/schema";
const cache = cacheExchange({
schema,
keys: {
// This is embedded in the `User` entity and has no ID
MatrixUser: () => null,
},
updates: {
Mutation: {
addEmail: (result, args: MutationAddEmailArgs, cache, _info) => {
const key = cache.keyOfEntity({
__typename: "User",
id: args.input.userId,
});
// Invalidate the emails field on the User object so that it gets refetched
const fields = cache
.inspectFields(key)
.filter((field) => field.fieldName === "emails");
for (const field of fields) {
cache.invalidate(key, field.fieldName, field.arguments);
}
},
removeEmail: (
result: { removeEmail?: RemoveEmailPayload },
args: MutationRemoveEmailArgs,
cache,
_info,
) => {
// Invalidate the email entity
cache.invalidate({
__typename: "UserEmail",
id: args.input.userEmailId,
});
// Let's try to figure out the userId to invalidate the emails field on the User object
const userId = result.removeEmail?.user?.id;
if (userId) {
const key = cache.keyOfEntity({
__typename: "User",
id: userId,
});
// Invalidate the emails field on the User object so that it gets refetched
const fields = cache
.inspectFields(key)
.filter((field) => field.fieldName === "emails");
for (const field of fields) {
cache.invalidate(key, field.fieldName, field.arguments);
}
}
},
verifyEmail: (
result: { verifyEmail?: VerifyEmailPayload },
args: MutationVerifyEmailArgs,
cache,
_info,
) => {
// Invalidate the email entity
cache.invalidate({
__typename: "UserEmail",
id: args.input.userEmailId,
});
// Let's try to figure out the userId to invalidate the emails field on the User object
const userId = result.verifyEmail?.user?.id;
if (userId) {
const key = cache.keyOfEntity({
__typename: "User",
id: userId,
});
// Invalidate the emails field on the User object so that it gets refetched
for (const field of cache
.inspectFields(key)
.filter((field) => field.fieldName === "emails")) {
cache.invalidate(key, field.fieldName, field.arguments);
}
}
},
},
},
});
const exchanges = [
// This sets the policy to 'cache-and-network' after 5 minutes
@@ -114,8 +20,8 @@ const exchanges = [
// This refetches all queries when the tab is refocused
refocusExchange(),
// The unified cache
cache,
// Simple cache
cacheExchange,
// Use `fetch` to execute the requests
fetchExchange,
+7 -1
View File
@@ -7,7 +7,13 @@
import { useState } from "react";
import * as z from "zod";
import type { PageInfo } from "./gql/graphql";
// PageInfo we get on connections from the GraphQL API
type PageInfo = {
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor?: string | null;
endCursor?: string | null;
};
export const FIRST_PAGE = Symbol("FIRST_PAGE");
export const LAST_PAGE = Symbol("LAST_PAGE");