Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(package/gqty): Use GraphQLError over plain objects in defaultResponseHandler #2058

Merged
merged 3 commits into from
Feb 3, 2025
Merged
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
5 changes: 5 additions & 0 deletions .changeset/angry-peaches-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'gqty': minor
---

Use GraphQLError over plain objects in defaultResponseHandler
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"[javascript][typescript][typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"typescript.tsdk": "node_modules/typescript/lib",
"prettier.proseWrap": "preserve"
}
27 changes: 8 additions & 19 deletions examples/gnt/gqty/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
*/

import { createLogger } from '@gqty/logger';
import { Cache, GQtyError, createClient, type QueryFetcher } from 'gqty';
import {
Cache,
createClient,
defaultResponseHandler,
type QueryFetcher,
} from 'gqty';
import {
type GeneratedSchema,
generatedSchema,
scalarsEnumsHash,
type GeneratedSchema,
} from './schema.generated';

const queryFetcher: QueryFetcher = async function (
Expand All @@ -29,23 +34,7 @@ const queryFetcher: QueryFetcher = async function (
...fetchOptions,
});

if (response.status >= 400) {
throw new GQtyError(
`GraphQL endpoint responded with HTTP status ${response.status}.`
);
}

const text = await response.text();

try {
return JSON.parse(text);
} catch {
throw new GQtyError(
`Malformed JSON response: ${
text.length > 50 ? text.slice(0, 50) + '...' : text
}`
);
}
return await defaultResponseHandler(response);
};

const cache = new Cache(
Expand Down
42 changes: 22 additions & 20 deletions examples/react/src/graphql/gqty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
import { createSubscriptionsClient } from '@gqty/subscriptions';
import extractFiles from 'extract-files/extractFiles.mjs';
import isExtractableFile from 'extract-files/isExtractableFile.mjs';
import { Cache, createClient, LegacyQueryFetcher as QueryFetcher } from 'gqty';
import {
generatedSchema,
Cache,
createClient,
defaultResponseHandler,
LegacyQueryFetcher as QueryFetcher,
} from 'gqty';
import {
GeneratedSchema,
generatedSchema,
scalarsEnumsHash,
SchemaObjectTypes,
SchemaObjectTypesNames,
} from './schema.generated';

const queryFetcher: QueryFetcher = async function (query, variables) {
Expand All @@ -33,10 +36,13 @@ const queryFetcher: QueryFetcher = async function (query, variables) {
formData.append(
'map',
JSON.stringify(
[...files.values()].reduce((prev, paths, i) => {
prev[i + 1] = paths;
return prev;
}, {} as Record<number, string[]>)
[...files.values()].reduce(
(prev, paths, i) => {
prev[i + 1] = paths;
return prev;
},
{} as Record<number, string[]>
)
)
);

Expand All @@ -51,7 +57,7 @@ const queryFetcher: QueryFetcher = async function (query, variables) {
mode: 'cors',
});

return await response.json();
return await defaultResponseHandler(response);
} else {
const response = await fetch(endpoint, {
method: 'POST',
Expand All @@ -65,13 +71,14 @@ const queryFetcher: QueryFetcher = async function (query, variables) {
mode: 'cors',
});

return await response.json();
return await defaultResponseHandler(response);
}
};

const subscriptionsClient =
typeof window !== 'undefined'
? createSubscriptionsClient({
typeof window === 'undefined'
? undefined
: createSubscriptionsClient({
wsEndpoint: () => {
// Modify if needed
const url = new URL('/api/graphql', window.location.href);
Expand All @@ -80,20 +87,15 @@ const subscriptionsClient =
console.log(42, url.href);
return url.href;
},
})
: undefined;
});

export const cache = new Cache(undefined, {
maxAge: 1000,
staleWhileRevalidate: 60 * 5000,
normalization: true,
});

export const client = createClient<
GeneratedSchema,
SchemaObjectTypesNames,
SchemaObjectTypes
>({
export const client = createClient<GeneratedSchema>({
cache,
schema: generatedSchema,
scalarsEnumsHash,
Expand All @@ -105,4 +107,4 @@ const { query, mutation, mutate, subscription, resolved, refetch, track } =
client;

export * from './schema.generated';
export { query, mutation, mutate, subscription, resolved, refetch, track };
export { mutate, mutation, query, refetch, resolved, subscription, track };
22 changes: 9 additions & 13 deletions examples/solid/src/gqty/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
*/

import { createSolidClient } from '@gqty/solid';
import { Cache, GQtyError, createClient, type QueryFetcher } from 'gqty';
import {
Cache,
createClient,
defaultResponseHandler,
GQtyError,
type QueryFetcher,
} from 'gqty';
import {
type GeneratedSchema,
generatedSchema,
scalarsEnumsHash,
type GeneratedSchema,
} from './schema.generated';

const queryFetcher: QueryFetcher = async function (
Expand Down Expand Up @@ -35,17 +41,7 @@ const queryFetcher: QueryFetcher = async function (
);
}

const text = await response.text();

try {
return JSON.parse(text);
} catch {
throw new GQtyError(
`Malformed JSON response: ${
text.length > 50 ? text.slice(0, 50) + '...' : text
}`
);
}
return await defaultResponseHandler(response);
};

const cache = new Cache(
Expand Down
6 changes: 2 additions & 4 deletions examples/vite-react/src/gqty/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { createReactClient } from '@gqty/react';
import type { QueryFetcher } from 'gqty';
import { Cache, createClient } from 'gqty';
import { Cache, createClient, defaultResponseHandler } from 'gqty';
import type { GeneratedSchema } from './schema.generated';
import { generatedSchema, scalarsEnumsHash } from './schema.generated';

Expand All @@ -27,9 +27,7 @@ const queryFetcher: QueryFetcher = async function (
...fetchOptions,
});

const json = await response.json();

return json;
return await defaultResponseHandler(response);
};

const cache = new Cache(
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fs.rmSync('dist', { force: true, recursive: true });
// tsc --emitDeclarationOnly
// Note: Creates the target directory, and serves as a pre-build typecheck.
tsc
.createProgram(['src/index.ts'], {
.createProgram(['src/envelop.ts', 'src/index.ts'], {
baseUrl: '.',
declaration: true,
emitDeclarationOnly: true,
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./envelop": {
"types": "./dist/envelop.d.ts",
"import": "./dist/envelop.mjs",
"require": "./dist/envelop.js"
}
},
"files": [
Expand Down
6 changes: 5 additions & 1 deletion packages/gqty/src/Cache/normalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ export const normalizeObject = <TData extends CacheObject>(

shells.add(result);

store.set(id, result);
store.set(id, result, {
// Has to use strong reference as this is usually the only reference,
// the cache is referencing the object via an internal identifier string.
strong: true,
});

return result;
}
Expand Down
18 changes: 15 additions & 3 deletions packages/gqty/src/Helpers/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ExecutionResult } from 'graphql';
import { GraphQLError, type ExecutionResult } from 'graphql';
import { GQtyError } from '../Error';

export const defaultResponseHandler = async (response: Response) => {
Expand Down Expand Up @@ -29,7 +29,15 @@ export const parseResponse = async (response: Response) => {
}

try {
return JSON.parse(text);
const result = JSON.parse(text);

if (Array.isArray(result?.errors)) {
result.errors = result.errors.map(
(error: any) => new GraphQLError(error.message, error)
);
Copy link

@kleberbaum kleberbaum Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GraphQLError Array

}

return result;
} catch {
throw new GQtyError(
`Received malformed JSON response from GraphQL endpoint: ${
Expand All @@ -56,7 +64,11 @@ export const isExecutionResult = (input: unknown): input is ExecutionResult => {

const value = input as Record<string, unknown>;

return 'data' in value || Array.isArray(value.errors);
return (
'data' in value ||
(Array.isArray(value.errors) &&
value.errors.every((error) => error instanceof GraphQLError))
);
};

export const handleResponseErrors = (result: ExecutionResult) => {
Expand Down
13 changes: 12 additions & 1 deletion packages/react/test/useMutation.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { renderHook, waitFor } from '@testing-library/react';
import { Cache } from 'gqty';
import { act } from 'react';
import { createReactTestClient } from './utils';

Expand Down Expand Up @@ -65,7 +66,17 @@ describe('useMutation', () => {
});

it('should update contents of useQuery via normalized cache', async () => {
const { useQuery, useMutation } = await createReactTestClient();
const { useQuery, useMutation } = await createReactTestClient(
undefined,
undefined,
undefined,
{
cache: new Cache(undefined, {
maxAge: Infinity,
normalization: true,
}),
}
);
const { result: q } = renderHook(() => {
const human = useQuery().human({ name: 'Uno' });

Expand Down
6 changes: 3 additions & 3 deletions packages/solid/src/query.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('createQuery', () => {
const { createQuery } = await createMockSolidClient({
client: {
cache: new Cache(undefined, {
maxAge: 0,
maxAge: 100,
staleWhileRevalidate: 5 * 30 * 1000,
}),
onFetch: ({ query }) => {
Expand Down Expand Up @@ -366,7 +366,7 @@ describe('createQuery', () => {
const { createQuery } = await createMockSolidClient({
client: {
cache: new Cache(undefined, {
maxAge: 0,
maxAge: 100,
staleWhileRevalidate: 5 * 30 * 1000,
}),
},
Expand Down Expand Up @@ -430,7 +430,7 @@ describe('createQuery', () => {
const { createQuery } = await createMockSolidClient({
client: {
cache: new Cache(undefined, {
maxAge: 0,
maxAge: 100,
staleWhileRevalidate: 5 * 30 * 1000,
}),
},
Expand Down
Loading
Loading