Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 140 lines 5.7 kB view raw view rendered
1--- 2title: Debugging 3order: 4 4--- 5 6# Debugging 7 8We've tried to make debugging in `urql` as seamless as possible by creating tools for users of `urql` 9and those creating their own exchanges. 10 11## Devtools 12 13It's easiest to debug `urql` with the [`urql` devtools.](https://github.com/urql-graphql/urql-devtools/) 14 15It offers tools to inspect internal ["Debug Events"](#debug-events) as they happen, to explore data 16as your app is seeing it, and to quickly trigger GraphQL queries. 17 18[For instructions on how to set up the devtools, check out `@urql/devtools`'s readme in its 19repository.](https://github.com/urql-graphql/urql-devtools) 20 21![Urql Devtools Timeline](../assets/devtools-timeline.png) 22 23## Debug events 24 25The "Debug Events" are internally what displays more information to the user on the devtools' 26"Events" tab than just [Operations](../api/core.md#operation) and [Operation 27Results](../api/core.md#operationresult). 28 29Events may be fired inside exchanges to add additional development logging to an exchange. 30The `fetchExchange` for instance will fire a `fetchRequest` event when a request is initiated and 31either a `fetchError` or `fetchSuccess` event when a result comes back from the GraphQL API. 32 33The [Devtools](#browser-devtools) aren't the only way to observe these internal events. 34Anyone can start listening to these events for debugging events by calling the 35[`Client`'s](../api/core.md#client) `client.subscribeToDebugTarget()` method. 36 37Unlike `Operation`s these events are fire-and-forget events that are only used for debugging. Hence, 38they shouldn't be used for anything but logging and not for messaging. **Debug events are also 39entirely disabled in production.** 40 41### Subscribing to Debug Events 42 43Internally the `devtoolsExchange` calls the `client.subscribeToDebugTarget`, but if we're looking to 44build custom debugging tools, it's also possible to call this function directly and to replace the 45`devtoolsExchange`. 46 47``` 48const { unsubscribe } = client.subscribeToDebugTarget(event => { 49 if (event.source === 'cacheExchange') 50 return; 51 console.log(event); // { type, message, operation, data, source, timestamp } 52}); 53``` 54 55As demonstrated above, the `client.subscribeToDebugTarget` accepts a callback function and returns 56a subscription with an `unsubscribe` method. We've seen this pattern in the prior ["Stream Patterns" 57section on the "Architecture" page.](../architecture.md) 58 59## Adding your own Debug Events 60 61Debug events are a means of sharing implementation details to consumers of an exchange. If you're 62creating an exchange and want to share relevant information with the `devtools`, then you may want 63to start adding your own events. 64 65#### Dispatching an event 66 67[On the "Authoring Exchanges" page](./authoring-exchanges.md) we've learned about the [`ExchangeInput` 68object](../api/core.md#exchangeinput), which comes with a `client` and a `forward` property. 69It also contains a `dispatchDebug` property. 70 71It is called with an object containing the following properties: 72 73| Prop | Type | Description | 74| ----------- | ----------- | ------------------------------------------------------------------------------------- | 75| `type` | `string` | A unique type identifier for the Debug Event. | 76| `message` | `string` | A human readable description of the event. | 77| `operation` | `Operation` | The [`Operation`](../api/core.md#operation) that the event targets. | 78| `data` | `?object` | This is an optional payload to include any data that may become useful for debugging. | 79 80For instance, we may call `dispatchDebug` with our `fetchRequest` event. This is the event that the 81`fetchExchange` uses to notify us that a request has commenced: 82 83```ts 84export const fetchExchange: Exchange = ({ forward, dispatchDebug }) => { 85 // ... 86 87 return ops$ => { 88 return pipe( 89 ops$, 90 // ... 91 mergeMap(operation => { 92 dispatchDebug({ 93 type: 'fetchRequest', 94 message: 'A network request has been triggered', 95 operation, 96 data: { 97 /* ... */ 98 }, 99 }); 100 101 // ... 102 }) 103 ); 104 }; 105}; 106``` 107 108If we're adding new events that aren't included in the main `urql` repository and are using 109TypeScript, we may also declare a fixed type for the `data` property, so we can guarantee a 110consistent payload for our Debug Events. This also prevents accidental conflicts. 111 112```ts 113// urql.d.ts 114import '@urql/core'; 115 116declare module '@urql/core' { 117 interface DebugEventTypes { 118 customEventType: { somePayload: string }; 119 } 120} 121``` 122 123Read more about extending types, like `urql`'s `DebugEventTypes` on the [TypeScript docs on 124declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html). 125 126### Tips 127 128Lastly, in summary, here are a few tips, that are important when we're adding new Debug Events to 129custom exchanges: 130 131-**Share internal details**: Frequent debug messages on key events inside your exchange are very 132 useful when later inspecting them, e.g. in the `devtools`. 133-**Create unique event types** : Key events should be easily identifiable and have a unique 134 names. 135-**Don't listen to debug events inside your exchange**: While it's possible to call 136 `client.subscribeToDebugTarget` in an exchange it's only valuable when creating a debugging 137 exchange, like the `devtoolsExchange`. 138-**Don't send warnings in debug events**: Informing your user about warnings isn't effective 139 when the event isn't seen. You should still rely on `console.warn` so all users see your important 140 warnings.