Giter Club home page Giter Club logo

urql's People

Contributors

0xflotus avatar alex-saunders avatar amyboyd avatar andyrichardson avatar baer avatar boygirl avatar danielrosenwasser avatar ddunc avatar dependabot[bot] avatar ebrillhart avatar frederikhors avatar github-actions[bot] avatar jevakallio avatar jonathancooperd avatar jovidecroock avatar kenwheeler avatar kingdutch avatar kiraarghy avatar kitten avatar michaelmerrilltest avatar mookiies avatar parkerziegler avatar paulathevalley avatar ryan-roemer avatar sarmeyer avatar theianjones avatar tptee avatar urql-ci avatar wgolledge avatar yankovalera avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

urql's Issues

What if a mutation affects more than one object?

It's mentioned in the README that "If you change a todo, return it. If you delete a todo, return it. If you add a todo, return it."
screen shot 2018-02-13 at 2 41 22 pm

I'm not a GraphQL guy, so correct me if what follows is discouraged in GraphQL universe, but I'm a CQRS guy, so when designing APIs, I make mutations (commands) that reflect the logic of the app, not align them 1-to-1 with the data entities.

The "add todo" (or "add anything else") is a simple mutation that by coincidence aligns 1-to-1 with the data entity it mutates.

In the real world, there may be more interesting mutations, for example, "signup" may want to create a new user profile record, a picture record, a greeting notification record, etc.

Thanks.

Issue with null fields

Hey,

I've just started implementing this library into a project, and it's really cool so far! I've run into an issue though.

If I make a query with a null field, I receive the following error:

TypeError: Cannot use 'in' operator to search for '__typename' in null
    at http://localhost:4001/static/js/bundle.js:52195:24
    at Array.map (<anonymous>)
    at getTypeNameFromField (http://localhost:4001/static/js/bundle.js:52193:20)
    at http://localhost:4001/static/js/bundle.js:52199:7
    at Array.map (<anonymous>)
    at getTypeNameFromField (http://localhost:4001/static/js/bundle.js:52193:20)
    at http://localhost:4001/static/js/bundle.js:52199:7
    at Array.map (<anonymous>)
    at getTypeNameFromField (http://localhost:4001/static/js/bundle.js:52193:20)
    at http://localhost:4001/static/js/bundle.js:52199:7
    at Array.map (<anonymous>)
    at getTypeNameFromField (http://localhost:4001/static/js/bundle.js:52193:20)
    at http://localhost:4001/static/js/bundle.js:52199:7
    at Array.map (<anonymous>)
    at getTypeNameFromField (http://localhost:4001/static/js/bundle.js:52193:20)
    at http://localhost:4001/static/js/bundle.js:52199:7
    at Array.map (<anonymous>)
    at getTypeNameFromField (http://localhost:4001/static/js/bundle.js:52193:20)
    at gankTypeNamesFromResponse (http://localhost:4001/static/js/bundle.js:52186:3)
    at http://localhost:4001/static/js/bundle.js:51930:128
    at <anonymous>

As soon as I remove that field, everything works again. Not sure if perhaps I am missing something, but figured I should pass this along. Thanks!

Option to debounce fetching a query

I have an auto-complete with Downshift and I render the list with Connect component from urql. It'd be great to have an option to debounce fetching from the server without blocking all the render method by wrapping the whole in a debounced component.

๐Ÿ’ฌ URQL future

I'm curious to know, do Formidable Labs have a long-term plan for maintaining urql as it gains more traction? Or it's considered a small side project and it will be?

My developer friends concern was this too since Apollo Client is actively maintained. I know @kenwheeler is taking care of it, but what about 1 year later or when new features are introduced to GraphQL.

So I think answering this might help a lot with the adoption.

Types

Hey, can we publish typescript types for this package?
I saw it is written in Typescript, so this should be easy. Using it in a Typescript code base not having types is a bummer.

Maybe we can just publish the raw .ts files but am not to confident about this option.

Support file uploads

If there is interest I can raise a PR to add support for file uploads via GraphQL mutation variables according to the GraphQL multipart request spec pretty easily.

It would add less than 1kb to the client bundle and the API will be unchanged so it won't get in the way for users who don't need uploads.

The logic to add to the fetcher is only a few lines; you can see how it works in apollo-upload-client.

I looked to see if there is some sort of middleware system that could be used as an alternative to baking it in, but there isn't one?

ShouldInvalidate on non mounted components

Hey first of i love the simplicity of urql.

It might be that i just discovered a bug or problem in design but it might as well be that i am just dump as f*ck.
If i have 2 Connect Components one using query to get data and one using mutation to update stuff that the other component read. It seems that as long as both Components are Rendered the mutation invalidates the cache and urql refetches the needed data so everything is is sync. But as soon as the read component is mounted then unmounted while the mutation is happening the cache does not get invalidated. I don't really could test is this was the case for the internal cache invalidation but it is definitely happening with shouldInvalidate because while we are not mounted the function can't be run. I have some ideas on how to fix this maybe but i want to make sure that i did not miss understand/use urql.

๐Ÿ’ฌ Making it work better for โš› Electron apps

Hey! I'm gonna do this myself today and I'll dig the source code too, but in the meantime, I'd like to know what do you think is the best way to do this.

What's the problem?

In Electron, each window is on its own separate BrowserWindow (exactly like a browser tab). This makes using Apollo or Redux across windows impossible, at least without implementing a utility on top.

KK, What's the question?

I thought maybe implementing a custom cache, and storing/retrieving it on electron-store (which is a universal store across the app) would allow me to have multiple instances of urql in multiple windows, all sharing a single source of truth.

Would that be OK from the urql side? What are the problems might occur with urql? Any thoughts welcome!

ty,
/cc @kenwheeler

Update cache for a query after mutation

As an example, I have a query (a list of things) in another part of the app, which I need to update the cache for it based on a mutation (add to list) in another part of the app.

I can return the whole data (the updated list) for that query so it gets updated, but I think if I can only return only the newly added thing, and manually add it to the list in the cache.

I don't know if it's a good idea or it's achievable in another way (plz lemme know). Thanks!

Could you please explain why it's not possible to use the returned data from the mutation for invalidation, instead of fetching from the server? Thanks :)

Feature idea: deduplication of requests

By default, Apollo dedupes requests that contain the same root queries using apollo-link-dedup.

urql already generates a key for the response cache, and that same key could be used for request deduplication. When response caching and request deduplication use the same underlying key, then your deduplication is as granular as your cache, which is cool ๐Ÿ˜Ž

So, for instance, right now urql could deduplicate the exact same query, but if #16 is ever implemented, then it could dedupe requests like the following:

{
  QueryA
}
{
  QueryA
  QueryB
}

I'm working on a lib called fetch-dedupe that abstracts apollo-link-dedupe's functionality into a generic-purpose lib, but for now here's the code.


If you were to copy pasta that file into urql, then the diff is actually pretty small to implement request deduplication:

- fetch(this.url, {
+ fetchDedupe(this.url, {
  body,
  headers: { 'Content-Type': 'application/json' },
  method: 'POST',
  ...fetchOptions,
- })
+ }, {
+   requestKey: hash,
+   responseType: 'json'
+ });
- .then(res => res.json())

although you might also want to add a new config that lets folks skip the deduplication, similar to Apollo's.

I'd PR a preview but I've never written TypeScript โœŒ๏ธ

Note: some modifications would likely need to be made to fetchDedupe to support sub-request body deduplication as described in #16

Calling one-off Queries

For most queries, the received data is bound to the component's props, so in essence, the app makes the data request, not the user. However, there are times when the user needs to manually make the data request, long after the component has been mounted (i.e. a user performs an inline search request that is not meant to be tied to or handled by, another child component). While for the most part I LOVE Apollo and am very appreciative of their OSS contribution, this is actually one area where they added too many extra steps for the developer... instead of directly supporting this, they came up with a very clunky "workaround" that requires a separate import:
import { withApollo } from 'react-apollo'

So since you're soliciting feedback :-) ... please directly support this instead of creating a clunky work-around for calling one-off/adhoc queries.

Potential performance gain in a babel-transform

This is specifically addressing the way urql injects __typename into queries at runtime. These transformations can be done statically at build time, rather than at runtime through a custom babel-transform.

What the babel transform would do is translate:

const TodoQuery = `
query {
  todos {
    id
    text
  }
}
`;

into

const TodoQuery = `
query {
  __typename
  todos {
    id
    text
  }
}
`;

(or whatever it needs to do, haven't traced the code entirely).

At that point, most of this file can go away.

My proposal is to do the following:

  1. Don't change the runtime code.
  2. Create a babel-transform that injects __typenames and shakes away the typenames.ts file so it doesn't even try to do any runtime transformations.

This approach gives a very lazy buy in where consumers don't have to use our babel-transform, but can use it to get a very specific optimization.

JSON.stringify vs immutable data structure

I see the library is using JSON.stringify which is probably a bit slow : https://github.com/FormidableLabs/urql/blob/3f859ed42d7a2ef2bff74c03e5245444425bd429/src/modules/client.ts#L116

Can't you do something like that:

const memoize = (fn) => {
 let prevArgs;
 let prevResult;
 return function(args) {
    if(args === prevArgs)
       return prevResult;
    prevArgs = arg;
    return prevResult = fn.call(this, arg);
 }
};

Instead of

const memoize = (fn) => {
 let cache = {};
 return function(args) {
 let hash = JSON.stringify(args); /* slow */
 if(hash in cache)
   return cache[hash];
 return cache[hash] = fn.call(this, arg);
 };
};

New Urql client per route

Urql is awesome, thanks for building it ๐ŸŽ‰

This is more of a question about recommended usage: should we create a new Urql client per route?

Currently I'm creating the client in the entrypoint to my SPA, so every component is wrapped in the provider. This makes e.g. authentication difficult, as (if the user isn't already logged in) I don't have the token when I call new Client().

Apollo lets you dynamically add headers to each requestโ€”is there any way to do something similar with Urql?

Would you recommend just having one client for the login route and another for the logged-in route once I have the auth token?

Query doesn't wait for Mutation to finish

I have a component like so:

<Connect
  query={query(getItemsQuery)}
  mutation={{
    updateItem: mutation(updateItemMutation)
  }}
  children={({ data, updateItem }) => {
  ...

When I fire the updateItem mutation, the getItems query is immediately reran, giving me items back before the mutation finishes. This results in a weird ux where a user edits a field, hits "save" (updateItem), and the field flashes back to it's old value, then to the new value again.

Is the query supposed to fire again before the mutation completes?

Mutations not working

Hey, my backend is using https://github.com/rmosolgo/graphql-ruby and when I run this mutation like so

const MarkCollectedMutation = `
mutation {
  updateItem(itemId: "6", collected: true, collectedBy: "Aria", collectedAt: 1234) {
    id
    collected
    collectedAt
    collectedBy
    code
  }
}
`;
  const onKeyDown = async e => {
    if (e.keyCode === 13) {
      console.log(item.id, e.target.value, Date.now());
      const data = await markCollected();
      console.log(data);
    }
  };
    <Connect
      query={query(ItemsQuery)}
      mutation={{
        markCollected: mutation(MarkCollectedMutation, { collected: true })
      }}
      children={({ loaded, fetching, refetch, data, error, markCollected }) => (

I get the error
Uncaught (in promise) {message: "No data"}
and the server logs

Started POST "/graphql" for ::1 at 2018-05-22 12:06:26 +1000
Processing by GraphqlController#execute as */*
  Parameters: {"query"=>"mutation {\n  updateItem(itemId: \"6\", collected: true, collectedBy: \"Aria\", collectedAt: 1234) {\n    id\n    collected\n    collectedAt\n    collectedBy\n    code\n    __typename\n  }\n}\n", "variables"=>{}, "graphql"=>{"query"=>"mutation {\n  updateItem(itemId: \"6\", collected: true, collectedBy: \"Aria\", collectedAt: 1234) {\n    id\n    collected\n    collectedAt\n    collectedBy\n    code\n    __typename\n  }\n}\n", "variables"=>{}}}
Completed 200 OK in 3ms (Views: 0.3ms | ActiveRecord: 0.0ms)

GraphQL Subscriptions

Looking at the todos for the project, there is nothing around subscriptions.

Are there any plans to add subscription support or at least giving a pointer for if you need subscriptions do not recommend this project?

Get Mutation Result in Promise

Should we add the mutation result to the resolved promise?
This will allow to do custom cache invalidation based on the full response from a mutation.

I would be pleased to tackle this in an pr if i get the ok

Mutation response promise resolves to undefined

I'm nearly certain I'm doing something wrong, but I can't figure out what it is. I'm trying to inspect the mutation result, but it keeps resolving to undefined. I read through the docs and every issue I could find that was related to mutations and couldn't figure out what I'm doing wrong, and based on the source it seems like I'm doing the right thing. I'm returning the created checkin object, so hopefully I haven't earned any plane crash sounds.

My mutation:

export const checkinFields = `
	id
	user_id
	user {
		display_name
	}
	lecture_id
	lecture {
		title
	}
`;

export const UserCheckin = mutation(`
	mutation($lecture_id: ID!, $attendance_code: String!) {
		userCheckin(lecture_id: $lecture_id, attendance_code: $attendance_code) {
			${checkinFields}
		}
	}
`);

My declaration:

<Connect
	query={query(`
		query ($after: Date, $before: Date) {
			lectures(after: $after, before: $before) {
				id
				title
				lecture_date_start
				lecture_date_end
				location {
					id
					name
				}
				presenters {
					id
					display_name
				}
				checkins {
					id
					user_id
				}
			}
		}
	`, { after: today, before: tonight })}
	mutation={{
		addCheckin: AddCheckin,
		userCheckin: UserCheckin
	}}
	children={({ loaded, data, addCheckin, userCheckin }) => { ... }}
/>

My caller:

	handleCheckin = async ({ attendance_code }, { setSubmitting, setFieldError }) => {
		const { lecture, userCheckin, navigate } = this.props;
		try {
			const what = userCheckin({ lecture_id: lecture.id, attendance_code });
			console.log(what);
			const hm = await what;
			console.log(hm);
		} catch (e) {
			logError(e);
			setFieldError(
				'attendance_code',
				"Sorry, it looks like that attendance code wasn't right. Please double check it and try again"
			);
			setSubmitting(false);
		}
	}

Dev tools screenshot, including console output and network response:
image

Thank you for your help!

Resolvers & WebSockets ETA Timeline

Just curious when Resolvers and hopefully support for Web Sockets is set to release (a soft roadmap to hope for, no hard dates needed of course). Apollo is already using resolvers to manage local application state, and also allows you to query/mutate your local store in the same fashion you would with a remote data store. What will be different or perhaps better?

Performance optimization: stitching together queries

Cool library, @kenwheeler ! I'm still checking out the code to see all of the features. I appreciate the simple approach you've taken here, particularly in comparison to the complexity of Apollo's source (I โค๏ธ Apollo, don't get me wrong! ๐Ÿ™‚ )


One thing I noticed is that if a user passes in 10 queries, 10 requests are made. I was thinking that it'd be neat if the queries could be stitched together.

Apollo and Relay can accomplish features like this by breaking the query down into an AST, which is powerful, but probably a little heavy-handed for what this library is doing. Luckily, the GraphQL syntax is (currently, at least) simple enough that there should be a way to do this robustly without an AST by making a few reasonable assumptions.

If you're ๐Ÿ‘ to this idea, I'd be happy to put together a proof of concept.


What lead me to this idea?

I noticed that urql caches at the query level, rather than the root query level. i.e.;

{
  queryA
  queryB
}

creates one entry in the cache, and not two.

I was going to open a similar issue to this one, suggesting that a lightweight function could be written that splits up the query for caching purposes. But then I realized that because the component accepts an array of queries, that maybe that was the recommended approach instead.

I wonder how hard it'd be to write these two functions: one that joins root queries, and the other that splits them apart.

What do you think?

Thanks for reading! โœŒ๏ธ

ClientEvent (or IEventFn) exports do not exist

I am seeing errors when bundling my app that uses urql v3-next1.

urql/es/interfaces/index.js 8:0-56 " './events' does not contain an export named 'ClientEvent'.

With urquelle the error is similar except with IEventFn. If you look at line 8 in the transpiled es/interfaces/index.js it's:

export { ClientEventType, ClientEvent } from "./events";

But events.js is just:

export var ClientEventType;

(function (ClientEventType) {
  ClientEventType["InvalidateTypenames"] = "InvalidateTypenames";
  ClientEventType["RefreshAll"] = "RefreshAll";
})(ClientEventType || (ClientEventType = {}));

It seems ClientEvent is completely removed when transpiling but the export in index.js is not.

Tagging the query/mutations to enable syntax highlighting possibility

Syntax highlighting libraries have no idea if a template string is GraphQL or not. In styled-components, they can detect it by css tag, or styled.X.

For GraphQL, highlighting extensions they can detect gql tagged strings. So I tend to define this:

const gql = String.raw

const UserQuery = gql`query { user }`

And I get syntax highlighting! (I use this in VSCode)

So as an idea we can refer to this method at the end of readme, as a "Syntax Highlighting" section, or we can export this from the library which might not be the best thing. I can do a PR as well.

Your thoughts? Thanks!

deploying with now or surge

Seems that deploying with either of these service gets you the following error. FYI

If you download the codebox (demo)[https://codesandbox.io/s/p5n69p23x0] and run it, it works without changing anything when running on dev local. When you build it, npm run build, and serve with serve -s build, or deploy it with now or surge, you get the issue noted below.

Error: Cannot use e "__Schema" from another module or realm.

Ensure that there is only one instance of "graphql" in the node_modules
directory. If different versions of "graphql" are the dependencies of other
relied on modules, use "resolutions" to ensure only one version is installed.

https://yarnpkg.com/en/docs/selective-version-resolutions

Duplicate "graphql" modules cannot be used at the same time since different
versions may have different capabilities and behavior. The data from one
version used in the function from another could produce confusing and
spurious results.

Hash usage?

I am curious to learn more about the decision to use a hashing function here. What value do you think it is adding?

Were it to be removed, these are the benefits that I would see:

  1. urql's bundle size is smaller
  2. urql is faster (though this would not impact users probably. Network requests aren't exactly sent off at 60fps or anything)

The key remains as unique as it needs to be either way, so the function of they key is equal between the two options.

One possible reason I can think of for it is that maybe graphql queries could get very large, so hashing them to a fixed string could be valuable. Apollo doesn't seem to worry about that, though, and it appears to be working fine for them (but maybe not?).

Anyway, I'm not necessarily recommending that you make this change. I'm just trying to learn more. Thanks for taking the time to read / respond!

Support Websockets

A tiny brief history: https://twitter.com/seanybingbong/status/958359534615056394

Currently I'm able to do all of my GraphQL transactions via websockets with Apollo Link like so:

const client = new ApolloClient({
  link: WebsocketLink(), // 
  cache: new InMemoryCache(),
});

I already need a websocket connection for my app so I thought I could use it to do all my GraphQL transactions since GraphQL doesn't care about transport. My app hardly has any Apollo-specific, so I thought I'd give urql a shot, but I'd still like to keep using websockets since the timing benefits over HTTP are nice (though, somewhat unnoticeable).

Maybe doing something like url: "wss://path.to/socket" in urql would trigger the use of websockets?

Support "extensions" property on received errors in next

urql is discarding the "errors" if a "data" is being returned. My GraphQL backend is returning {"errors":[{"message":"Please, enter valid credentials","locations":[{"line":2,"column":3}],"path":["tokenAuth"]}],"data":{"tokenAuth":null}} when attempting to get a JWT while logging in with invalid credentials. It appears that in this is in http-exchange.ts.https://github.com/FormidableLabs/urql/blob/5e113c45f730223cc2d11125aa968ebc4eb260fd/src/modules/http-exchange.ts#L66-L76 .

It seems this issue of both data and errors was faced before in graphql-go/graphql#103

RFC: Make "consumer" components reactive to cache changes

In the next branch we already moved a lot of the caching logic to an exchange. It's basically almost equivalent. If we now introduce a more complete "watching" mechanism, then components can react to changes in the cache, which allows us to move all caching logic to an exchange and completely remove it from components.

This also has the nice side-effect of users being able to introduce 100% new caching logic via a custom cache exchange. This could pave way for a normalized cache a la Apollo, which is opt-in as an exchange.

cc @kenwheeler: Let's discuss the potentials of this and its feasibility pre-0.3?

3-Way Data Binding

For this to work, it can't just be the client listening for changes on the server, but the server has to also be listening for changes in the local store, in order for this to work.

I know this feature suggestion sounds a bit premature/ridiculous, but it's only a matter of time before someone comes out with it, so why not Formidable haha?! I know this is done via web sockets between Angular and Firebase, but here's what that might look like in the React/GraphQL world:

https://docs.google.com/presentation/d/1NjjbYW-TaAdxyydwcGx9BoP2eSVp2Sh6hMzmxrOstDU/edit?usp=sharing

adding an onError prop ?

Hi, thanks for making urql, it makes using graphql a breeze.
I have a Feature Request for adding an error handler

Feature Request
How about adding an onError prop to connect/ or a configuration option directly at Client maybe.
eg:

<Connect
    query={query(TodoQuery)}
    mutation={{
      addTodo: mutation(AddTodo),
    }}
    onError={ error => doSomethingOnError(error) }
    children={({ loaded, fetching, refetch, data, error, addTodo }) => {
      //...Your Component
    }}
  />

or

const client = new Client({
  url: 'http://localhost:3001/graphql',
  onError: (error => doSomethingOnError(error))
});

Usecase
if i have access to onError, i can change route (404/500) / fire a redux action / log error message / send error to sentry.

Possible Implementation
possible by passing the error handler function to query and mutation
https://github.com/FormidableLabs/urql/blob/master/src/modules/client.ts#L159
https://github.com/FormidableLabs/urql/blob/master/src/modules/client.ts#L202

If this feature is to be considered, i can try making a PR

Testing urql-wrapped components

I realise this may not be the right place to ask, but I'm only getting started with GraphQL and urql, so sorry if this is an obvious question.

I have a index.tsx which sets up the urql Provider and Connect and subsequently renders MyComponent:

import { Connect, Provider, UrqlProps } from "urql";

const myMutationString = `string here`;

interface Mutations {
  myMutation: (text: string) => void;
}

<Provider client={client}>
  <Connect
    mutation={{myMutation: mutation(myMutationString)}}
    children={(urqlProps: UrqlProps<{}, Mutations>) => (
      <MyComponent
        {...urqlProps}
        myProp="123"
      />
    )}
  />
</Provider>

Subsequently I've typed the Props from MyComponent as an intersection between UrqlProps<{}, Mutations> and the props specific to MyComponent (such as myProp in the example above).

The nice way about setting it up this way (I got the idea from the Redux community and how they separate redux containers that do the connect and the component itself), is that MyComponent doesn't need to know that the loaded, data, etc props came from urql, and can simply use these props as it sees fit.

Now the hitch: I'm trying to test MyComponent using enzyme and jest, and trying to simply pass it the urql props it's actually using (in this case, just myMutation):

const component = shallow(
    <MyComponent
      myMutation={jest.fn()}
      myProp="123"
    />
)

...but I'm of course running into a Typescript error for not passing down everything defined by urql (such as cache, fetching, etc, as defined here)

Does anyone have any advice about typing urql-wrapped components and subsequently testing them? Thanks in advanced ๐Ÿ˜„

Caching policy

I've used Apollo extensively over the last year for almost all of our projects and I think the caching policy they use is very well attuned to the fundamental structure of a node graph.

Each item in the graph is cached according to its typename and id, which means that as long as you have a unique id within a type - something that is a fundamental part of storing data in a database to begin with - then you have a way to tell all nodes apart. GraphQL has built into the specification the best practice of returning mutated items as well, which solves the cache invalidation problem on a very granular level.

Basically, I think the guys over at Apollo nailed caching for GraphQL and it leaves me wondering why urql has decided to do a very expensive greedy invalidation approach. I think there are cases where it might make sense, but in general, the Apollo method is more complete. What might make sense is to have a more configurable caching policy. The current method of hashing just the query and variables prevents more advanced caching policies like this.

Thoughts?

Devtools Extension

We should investigate a path for building installable devtools for better introspection into queries, viewing/resetting cache, perhaps logging, maybe a graphiql interface.

As a basic MVP, we could build a module to log urql events in the console. Beyond that, a module that is available as an installable chrome/firefox plugin.

Use of functional children prop instead of render prop

Since the react official context api is using functional render prop won't it be a better or standard approach to follow instead of a render prop. Also it becomes easier to read/compose.
Accordingly given here
reactjs/rfcs#2
This is how the code will look like,

const Home = () => (
  <Connect query={query(TodoQuery)} mutation={{ addTodo: mutation(AddTodo)}}>
    {({ loaded, fetching, refetch, data, error, addTodo }) => {
      //...Your Component
    }}
  </Connect>
);

We can also take a look at other graphql libraries like apollo who are also following this pattern.
https://github.com/department-stockholm/apollo-component/

client-component subscribes repeatedly to client

Hi, I wish I had more information to give you in this issue. I've been trying to debug this issue for a couple of hours now, and I haven't been able to figure out what the root cause is.

I've got a component (I've tried using the ConnectHOC, and the Connect component) which is connected. It has one query, and two mutations. Every time I execute a mutation, a new subscription gets created on the client for the query. The query never changes during this exercise. Each of these subscriptions executes the same query. Running a mutation causes all the subscriptions to refetch, which hammers my server with an increasing number of identical GraphQL queries. I cannot for the life of me figure out what I am doing that would cause this. Have you seen anything like this before?

RFC: Fragment-level caching

Instead of shipping a complex, normalising cache by default (it might still be implemented as a third-party exchange, I suppose) we can ship a simple fragment-level cache.

This caching strategy would live alongside the current query-level cache. It can also use the same store/cache entity and just use separate keys.

Strategy

  • Detect queries containing fragments
  • Inspect each response for each of the given fragments
  • Cache given fragments by "Fragment + Typename + ID" (default key extractor)
  • Don't invalidate these keys like cached queries are, instead keep them up-to-date with each incoming response

Hence a most-recent version of a cached fragment will always be available.

Usage

Once this cache is in place we're able to integrate primitive APIs to access these cached fragments. On a high-level API, we can introduce a FragmentCache component, used like so:

<FragmentCache fragment={fragmentStr} query={{ __typename: 'Item', id: 'test' }}>
  {({ data, stale }) => ()}
</FragmentCache>

An example can be found here: https://gist.github.com/kitten/3a9136fa731678838013292a32f7daaa

Optimistic updates can also at this point be introduced. A mutation could specify optimisticFragments, which provide a list of optimistic responses from the mutation which only update the fragment-level cache.

Why?

  • We'd avoid a complex cache by default and instead have two highly predictable caches
  • We still provide the ability to solve the "90%" case, accessing the cache explicitly when a query is still loading or other custom use-cases
  • The cache component can be adapted to also work for the query component, thus giving us some breathing-room for changing the default cache/network-control behaviour (cache-first instead of -only?)

How?

We can maybe implement this as a separate package first, since it's relatively contained, so that it's possible to try this concept out without changing urql's public API at all.

It could be published as urql-exchange-fragment-cache or sth

cc @kenwheeler

Infra: Add `npm version` workflow

  • preversion: run tests and lint
  • version: build
  • update docs to say all of this.

Suggested verbiage:

## Release

_(Only for project administrators)_

We use an `npm version` release workflow:

1. Run `npm version patch` (or `minor|major|ACTUAL_VERSION_NUMBER`) which runs
   tests/lint, builds all files we need to publish, mutates `package.json`,
   and does all the requisite git stuff.
2. Run `npm publish` and publish to npm if all is well.
3. Run `git push && git push --tags`

We've had failed publishes with nothing in them (see #107 )

Queries fail when fetchOptions headers overwritten

I have overriden my fetch options like so

const client = new Client({
  url: 'http://localhost:4000/',
  fetchOptions: () => {
    const token = window.localStorage.getItem('token');
    console.log(token);
    if (token) {
      return {
        headers: {
          Authorization: `Bearer ${token}`
        }
      };
    }
    return {};
  }
});

And once token isn't null, it makes requests at plain/text instead of application/json. This is tracked down to https://github.com/FormidableLabs/urql/blob/1564d4d713059f5acf25412a7d334209d4d7b102/src/modules/client.ts#L196 in which it spreads over headers.

I fixed this clientside by adding

          'Content-Type': 'application/json'

to my fetchOptions, but this could be fixable by deep merging fetchOptions

Can ConnectHOC be a PureComponent?

I was running into an issue earlier today where I noticed that one of my components was rerendering much more frequently than I expected it to. It was hooked up to the ConnectHOC. I swapped out my component implementation for one that used the Connect component, and my rerendering problems went away. I checked, and ConnectHOC is extending Component instead of PureComponent.

I'm not a React expert, so I could be way wrong. But I think the ConnectHOC should be able to be a PureComponent. But if it can't, it would probably be good to define some kind of shouldComponentUpdate definition for it.

I'm happy to submit a PR to add it, but I always like to get feedback to make sure my proposed changes are wanted/make sense before I start working on a PR.

Fetching data on the server, or SSR in other words

I get why SRR is low on the prioritization list seing you guys have other features to bake first, but I'm wondering where it is in your pipeline and what your initial thoughts are for implementing it.

And how can we help?

Codesandbox demo is not working

https://codesandbox.io/s/p5n69p23x0.

Response

{
  "code": 400,
  "error": "Script returned an error.",
  "details": "Error: Schema must be an instance of GraphQLSchema. Also ensure that there are not multiple versions of GraphQL installed in your node_modules directory.",
  "name": "Error",
  "message": "Schema must be an instance of GraphQLSchema. Also ensure that there are not multiple versions of GraphQL installed in your node_modules directory.",
  "stack": "Error: Schema must be an instance of GraphQLSchema. Also ensure that there are not multiple versions of GraphQL installed in your node_modules directory.\n    at invariant (/data/_verquire/graphql/0.11.7/node_modules/graphql/jsutils/invariant.js:18:11)\n    at validate (/data/_verquire/graphql/0.11.7/node_modules/graphql/validation/validate.js:59:72)\n    at /data/_verquire/graphql/0.11.7/node_modules/graphql/graphql.js:69:51\n    at new Promise (<anonymous>)\n    at graphqlImpl (/data/_verquire/graphql/0.11.7/node_modules/graphql/graphql.js:59:10)\n    at Object.graphql (/data/_verquire/graphql/0.11.7/node_modules/graphql/graphql.js:48:227)\n    at /data/io/bd8d6406-646c-4a14-8377-1a244f3dba80/webtask.js:140:26\n    at <anonymous>\n    at process._tickDomainCallback (internal/process/next_tick.js:228:7)"
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.