Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1--- 2title: Core / Node.js 3order: 3 4--- 5 6# Core and Node.js Usage 7 8The `@urql/core` package contains `urql`'s `Client`, some common utilities, and some default 9_Exchanges_. These are the shared, default parts of `urql` that we will be using no matter which 10framework we're interacting with. 11 12All framework bindings — meaning `urql`, `@urql/preact`, `@urql/svelte`, and `@urql/vue` — reexport 13all exports of our `@urql/core` core library. This means that if we want to use `urql`'s `Client` 14imperatively or with Node.js we'd use `@urql/core`'s utilities or the `Client` directly. 15 16In other words, if we're using framework bindings then writing `import { Client } from "@urql/vue"` 17for instance is the same as `import { Client } from "@urql/core"`. 18This means that we can use the core utilities and exports that are shared between all bindings 19directly or install `@urql/core` separately. We can even use `@urql/core` directly without any 20framework bindings. 21 22## Installation 23 24As we said above, if we are using bindings then those will already have installed `@urql/core` as 25they depend on it. They also all re-export all exports from `@urql/core`, so we can use those 26regardless of which bindings we've installed. However, it's also possible to explicitly install 27`@urql/core` or use it standalone, e.g. in a Node.js environment. 28 29```sh 30yarn add @urql/core 31# or 32npm install --save @urql/core 33``` 34 35Since all bindings and all exchanges depend on `@urql/core`, we may sometimes run into problems 36where the package manager installs _two versions_ of `@urql/core`, which is a duplication problem. 37This can cause type errors in TypeScript or cause some parts of our application to bundle two 38different versions of the package or use slightly different utilities. We can fix this by 39deduplicating our dependencies. 40 41```sh 42# npm 43npm dedupe 44# pnpm 45pnpm dedupe 46# yarn 47npx yarn-deduplicate && yarn 48``` 49 50## GraphQL Tags 51 52A notable utility function is the `gql` tagged template literal function, which is a drop-in 53replacement for `graphql-tag`, if you're coming from other GraphQL clients. 54 55Wherever `urql` accepts a query document, we can either pass a string or a `DocumentNode`. `gql` is 56a utility that allows a `DocumentNode` to be created directly, and others to be interpolated into 57it, which is useful for fragments for instance. This function will often also mark GraphQL documents 58for syntax highlighting in most code editors. 59 60In most examples we may have passed a string to define a query document, like so: 61 62```js 63const TodosQuery = ` 64 query { 65 todos { 66 id 67 title 68 } 69 } 70`; 71``` 72 73We may also use the `gql` tag function to create a `DocumentNode` directly: 74 75```js 76import { gql } from '@urql/core'; 77 78const TodosQuery = gql` 79 query { 80 todos { 81 id 82 title 83 } 84 } 85`; 86``` 87 88Since all framework bindings also re-export `@urql/core`, we may also import `gql` from `'urql'`, 89`'@urql/svelte'` and other bindings directly. 90 91We can also start interpolating other documents into the tag function. This is useful to compose 92fragment documents into a larger query, since it's common to define fragments across components of 93an app to spread out data dependencies. If we accidentally use a duplicate fragment name in a 94document, `gql` will log a warning, since GraphQL APIs won't accept duplicate names. 95 96```js 97import { gql } from '@urql/core'; 98 99const TodoFragment = gql` 100 fragment SmallTodo on Todo { 101 id 102 title 103 } 104`; 105 106const TodosQuery = gql` 107 query { 108 todos { 109 ...TodoFragment 110 } 111 } 112 113 ${TodoFragment} 114`; 115``` 116 117This usage will look familiar when coming from the `graphql-tag` package. The `gql` API is 118identical, and its output is approximately the same. The two packages are also intercompatible. 119However, one small change in `@urql/core`'s implementation is that your fragment names don't 120have to be globally unique, since it's possible to create some one-off fragments occasionally, 121especially for `@urql/exchange-graphcache`'s configuration. 122It also pre-generates a "hash key" for the `DocumentNode` which is what `urql` does anyway, thus 123avoiding some extra work compared to when the `graphql-tag` package is used with `urql`. 124 125## Using the `urql` Client 126 127The `Client` is the main "hub" and store for everything that `urql` does. It is used by all 128framework bindings and from the other pages in the "Basics" section we can see that creating a 129`Client` comes up across all bindings and use-cases for `urql`. 130 131[Read more about the `Client` and `urql`'s architecture on the "Architecture" 132page.](../architecture.md) 133 134### Setting up the `Client` 135 136The `@urql/core` package exports a `Client` class, which we can use to 137create the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 138 139```js 140import { Client, cacheExchange, fetchExchange } from '@urql/core'; 141 142const client = new Client({ 143 url: 'http://localhost:3000/graphql', 144 exchanges: [cacheExchange, fetchExchange], 145}); 146``` 147 148At the bare minimum we'll need to pass an API's `url`, and the `fetchExchange`, 149when we create a `Client` to get started. 150 151Another common option is `fetchOptions`. This option allows us to customize the options that will be 152passed to `fetch` when a request is sent to the given API `url`. We may pass in an options object, or 153a function returning an options object. 154 155In the following example we'll add a token to each `fetch` request that our `Client` sends to our 156GraphQL API. 157 158```js 159const client = new Client({ 160 url: 'http://localhost:3000/graphql', 161 exchanges: [cacheExchange, fetchExchange], 162 fetchOptions: () => { 163 const token = getToken(); 164 return { 165 headers: { authorization: token ? `Bearer ${token}` : '' }, 166 }; 167 }, 168}); 169``` 170 171### The `Client`s options 172 173As we've seen above, the most important options for the `Client` are `url` and `exchanges`. 174The `url` option is used by the `fetchExchange` to send GraphQL requests to an API. 175 176The `exchanges` option is of particular importance however because it tells the `Client` what to do 177with our GraphQL requests: 178 179```js 180import { Client, cacheExchange, fetchExchange } from '@urql/core'; 181 182const client = new Client({ 183 url: 'http://localhost:3000/graphql', 184 exchanges: [cacheExchange, fetchExchange], 185}); 186``` 187 188For instance, here, the `Client`'s caching and fetching features are only available because we're 189passing it exchanges. In the above example, the `Client` will try to first read a GraphQL request 190from a local cache, and if this request isn't cached it'll make an HTTP request. 191The caching in `urql` is also implemented as an exchange, so for instance, the behavior described 192on the ["Document Caching" page](./document-caching.md) is all contained within the `cacheExchange` 193above. 194 195Later, [in the "Advanced" section](../advanced/README.md) we'll see many more features that `urql` 196supports by adding new exchanges to this list. On [the "Architecture" page](../architecture.md) 197we'll also learn more about what exchanges are and why they exist. 198 199### One-off Queries and Mutations 200 201When you're using `urql` to send one-off queries or mutations — rather than in full framework code, 202where updates are important — it's common to convert the streams that we get to promises. The 203`client.query` and `client.mutation` methods have a shortcut to do just that. 204 205```js 206const QUERY = ` 207 query Test($id: ID!) { 208 getUser(id: $id) { 209 id 210 name 211 } 212 } 213`; 214 215client 216 .query(QUERY, { id: 'test' }) 217 .toPromise() 218 .then(result => { 219 console.log(result); // { data: ... } 220 }); 221``` 222 223In the above example we're executing a query on the client, are passing some variables and are 224calling the `toPromise()` method on the return value to execute the request immediately and get the 225result as a promise. This may be useful when we don't plan on cancelling queries, or we don't 226care about future updates to this data and are just looking to query a result once. 227 228This can also be written using async/await by simply awaiting the return value of `client.query`: 229 230```js 231const QUERY = ` 232 query Test($id: ID!) { 233 getUser(id: $id) { 234 id 235 name 236 } 237 } 238`; 239 240async function query() { 241 const result = await client.query(QUERY, { id: 'test' }); 242 console.log(result); // { data: ... } 243} 244``` 245 246The same can be done for mutations by calling the `client.mutation` method instead of the 247`client.query` method. 248 249It's worth noting that promisifying a query result will always only give us _one_ result, because 250we're not calling `subscribe`. This means that we'll never see cache updates when we're asking for 251a single result like we do above. 252 253#### Reading only cache data 254 255Similarly there's a way to read data from the cache synchronously, provided that the cache has 256received a result for a given query before. The `Client` has a `readQuery` method, which is a 257shortcut for just that. 258 259```js 260const QUERY = ` 261 query Test($id: ID!) { 262 getUser(id: $id) { 263 id 264 name 265 } 266 } 267`; 268 269const result = client.readQuery(QUERY, { id: 'test' }); 270 271result; // null or { data: ... } 272``` 273 274In the above example we call `readQuery` and receive a result immediately. This result will be 275`null` if the `cacheExchange` doesn't have any results cached for the given query. 276 277### Subscribing to Results 278 279GraphQL Clients are by their nature "reactive", meaning that when we execute a query, we expect to 280get future results for this query. [On the "Document Caching" page](./document-caching.md) we'll 281learn how mutations can invalidate results in the cache. This process (and others just like it) can 282cause our query to be refetched. 283 284In essence, if we're subscribing to results rather than using a promise, like we've seen above, then 285we're able to see future changes for our query's results. If a mutation causes a query to be 286refetched from our API in the background then we'll see a new result. If we execute a query 287somewhere else then we'll get notified of the new API result as well, as long as we're subscribed. 288 289```js 290const QUERY = ` 291 query Test($id: ID!) { 292 getUser(id: $id) { 293 id 294 name 295 } 296 } 297`; 298 299const { unsubscribe } = client.query(QUERY, { id: 'test' }).subscribe(result => { 300 console.log(result); // { data: ... } 301}); 302``` 303 304This code example is similar to the one before. However, instead of sending a one-off query, we're 305subscribing to the query. Internally, this causes the `Client` to do the same, but the 306subscription means that our callback may be called repeatedly. We may get future results as well as 307the first one. 308 309This also works synchronously. As we've seen before `client.readQuery` can give us a result 310immediately if our cache already has a result for the given query. The same principle applies here! 311Our callback will be called synchronously if the cache already has a result. 312 313Once we're not interested in any results anymore, we need to clean up after ourselves by calling 314`unsubscribe`. This stops the subscription and makes sure that the `Client` doesn't actively update 315the query anymore or refetches it. We can think of this pattern as being very similar to events or 316event hubs. 317 318We're using [the Wonka library for our streams](https://wonka.kitten.sh/basics/background), which 319we'll learn more about [on the "Architecture" page](../architecture.md). But we can think of this as 320React's effects being called over time, or as `window.addEventListener`. 321 322## Common Utilities in Core 323 324The `@urql/core` package contains other utilities that are shared between multiple addon packages. 325This is a short but non-exhaustive list. It contains, 326 327- [`CombinedError`](../api/core.md#combinederror) - our abstraction to combine one or more `GraphQLError`(s) and a `NetworkError` 328- `makeResult` and `makeErrorResult` - utilities to create _Operation Results_ 329- [`createRequest`](../api/core.md#createrequest) - a utility function to create a request from a 330 query, and some variables (which generate a stable _Operation Key_) 331 332There are other utilities not mentioned here. Read more about the `@urql/core` API in the [API docs](../api/core.md). 333 334## Reading on 335 336This concludes the introduction for using `@urql/core` without any framework bindings. This showed 337just a couple of ways to use `gql` or the `Client`, however you may also want to learn more about 338[how to use `urql`'s streams](../architecture.md#stream-patterns-in-urql). Furthermore, apart from the framework 339binding introductions, there are some other pages that provide more information on how to get fully 340set up with `urql`: 341 342- [How does the default "document cache" work?](./document-caching.md) 343- [How are errors handled and represented?](./errors.md) 344- [A quick overview of `urql`'s architecture and structure.](../architecture.md) 345- [Setting up other features, like authentication, uploads, or persisted queries.](../advanced/README.md)