Comments (3)
Including it in gql.tada would definitely be a step in the right direction.
But I was thinking since graphcache already supports a schma aware mode, this could be implemented with any schema that is supplied to graphcache, regardless of where it comes from.
This would decouple it completely from gql.tata and make it compatible with other graphql type generators
from urql.
This is definitely something we intend to include in gql.tada
. The only issue I can foresee is that the introspection output there isn't 100% stabilised, and we plan to actually output the pre-formatted version of it as well.
So, if we write up a version that's targeting the current output in gql.tada
(from mapIntrospection
) that'd likely help with performance of inference in the future.
There's also some old issues to revisit with the typings and how they're being consumed by Graphcache, but that's likely TBD until we explore this again.
But yea, I'd currently favour absorbing the complexity here into a gql.tada
sub-module (under maybe gql.tada/addons/graphcache
maybe)
cc @JoviDeCroock
from urql.
I figured I'd add what I've been patching into my current project as it might be useful in further implementing this feature.
When using Cache.resolve
, I noticed that sometimes a scalar value is returned and other times a reference is returned if the result would have been an object due to the cache normalizing data. It also seemed like it would be nice if there was type-aware autocompletion regarding the field
parameter of the resolve function as well, so I came up with the following:
Cache Normalization
import type { DataField, Entity, FieldArgs } from '@urql/exchange-graphcache';
/**
* This utility type represents a reference returned from `Cache.resolve`,
* typically looking like `Book:123` but with the full type being referenced
* stored alongside it in branded-type fashion
*/
type Reference<T> = string & { __def: T };
/**
* This utility type is used to "normalize" an object type and return it
* as either a reference type or a scalar.
*/
type Normalize<T> =
T extends Array<infer U>
? Array<Normalize<U>>
: T extends object // Check if T can be normalized
? Reference<T> // If it can, return the normalized reference type
: T; // If it doesn't, return T
// Example
type Book = {
__typename: 'Book';
id: string;
title: string;
publicationDate: string;
author: {
__typename: 'Author';
id: string;
name: string;
};
};
type BookReference = Reference<Book>;
// ▲ string & { __def: Book; }
Helpers
/**
* This helper casts any `Entity` to a reference type, essentially prepping it
* for use in `Cache.resolve` without actually modifying its underlying type.
*/
const toReference = <T>(entity: Entity): Reference<T> => entity as Reference<T>;
/**
* This utility type is used to resolve a reference type to its full type
*/
type Resolved<T extends Reference<any>> = T extends Reference<infer U> ? U : T;
@urql/exchange-graphcache
Type Override
By overriding the definition of the Cache.resolve
function, I am able to
hook into the type E
of the entity
parameter, which then gives me access to the underlying type being obfuscated by Reference
. This can then be leveraged to add autocompletion of the field
parameter by limiting it to properties that exist on the resolved type of E
. The return type will be the normalized value of the property accessed via F
, which will either be a scalar or another Reference
.
declare module '@urql/exchange-graphcache' {
interface Cache {
// Custom typing
resolve<E extends Reference<any>, F extends keyof Resolved<E>>(
entity: E | undefined,
field: F, // You get type completion here
args?: FieldArgs,
): Normalize<Resolved<E>[F]>;
// Original typing
resolve(entity: Entity | undefined, fieldName: string, args?: FieldArgs): DataField | undefined;
}
}
Usage
Within an optimistic update:
type Book = {
__typename: 'Book';
id: string;
title: string;
publicationDate: string;
author: {
__typename: 'Author';
id: string;
name: string;
};
};
const cache = cacheExchange({
optimistic: {
updateBook: (args, cache) => {
const bookRef = toReference<Book>({ __typename: 'Book', id: args.id });
// ▲ Reference<Book>
return {
__typename: 'Book',
id: args.id,
title: args.title,
author: () => {
// ▼ intellisense limits this to keyof Book
const authorRef = cache.resolve(bookRef, 'author');
// ▲ Reference<{
// __typename: 'Author';
// id: string;
// name: string;
// }>
return {
__typename: 'Author',
// ▼ intellisense limits this to '__typename' | 'id' | 'name'
id: cache.resolve(authorRef, 'id'), // string
// ▼ intellisense limits this to '__typename' | 'id' | 'name'
name: () => cache.resolve(authorRef, 'name'), // string
};
},
};
},
},
});
from urql.
Related Issues (20)
- Passing a `Headers` object results in an error
- Possible memory leak when using multiple queries on the same page HOT 7
- requestPolicyExchange does not re-request in production HOT 3
- cacheExchange - cache.readQuery (updateQuery) can't find queries with aliased variables HOT 1
- @urql/exchange-persisted Module name, 'crypto' does not resolve to a valid URL. HOT 7
- Non RSC authExchange failing HOT 2
- Problem: @urql/exchange-graphcache updateCacheWithResult reads null after writing link in cache updater. HOT 8
- Invalidating cache in Graphcache doesn't trigger query refetch in Next.js app router HOT 2
- RFC: react native file upload support HOT 8
- Introspected schema from urql-introspection doesn't match type of exchange-graphcache HOT 1
- Graphcache: mutations with different files in parallel, results lost HOT 3
- Invalidate with a key does not produce expected result HOT 2
- RFC: Normalize embedded documents based on parent HOT 5
- Syntax Error: Unexpected token in SelectionSet HOT 1
- RFC: @urql/vue - Allow getter syntax in `useQuery` HOT 4
- RFC: Add secondary keys HOT 2
- @urql/exchange-graphcache returns empty data & error although fetch response contains valid data. (when same entity is requested in parallel) HOT 1
- Multiple requests when using react suspense HOT 13
- ExecuteExchange doesn't interact well with "server-only" code HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from urql.