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
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.