Skip to content

Commit ab84ac8

Browse files
authored
Merge pull request #671 from appsignal/add-urql-integration
Add urql integration package
2 parents e4f3b7c + 46f4060 commit ab84ac8

11 files changed

Lines changed: 436 additions & 0 deletions

File tree

package-lock.json

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/urql/.changesets/.gitkeep

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
bump: major
3+
type: add
4+
---
5+
6+
Our new `@appsignal/urql` package allows reporting all GraphQL errors automatically through a custom `urql` exchange:
7+
8+
```javascript
9+
import { createClient, fetchExchange } from 'urql';
10+
import Appsignal from '@appsignal/javascript';
11+
import { createAppsignalExchange } from '@appsignal/urql';
12+
13+
const appsignal = new Appsignal({
14+
key: 'YOUR FRONTEND API KEY'
15+
});
16+
17+
const client = createClient({
18+
url: 'https://api.example.com/graphql',
19+
exchanges: [createAppsignalExchange(appsignal), fetchExchange]
20+
});
21+
```

packages/urql/README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# `@appsignal/urql`
2+
3+
- [AppSignal.com website][appsignal]
4+
- [Documentation][docs]
5+
- [Support][contact]
6+
7+
The `@appsignal/javascript` integration for urql GraphQL client.
8+
9+
See also the [mono repo README](../../README.md) for more information.
10+
11+
## Installation
12+
13+
Add the `@appsignal/urql` and `@appsignal/javascript` packages to your `package.json`. Then, run `yarn install`/`npm install`.
14+
15+
You can also add these packages to your `package.json` on the command line:
16+
17+
```
18+
yarn add @appsignal/javascript @appsignal/urql urql wonka
19+
npm install --save @appsignal/javascript @appsignal/urql urql wonka
20+
```
21+
22+
## Usage
23+
24+
### Urql Exchange
25+
26+
The `@appsignal/urql` package provides a custom urql exchange that automatically reports GraphQL errors to AppSignal. This exchange intercepts all query and mutation results and reports any errors without requiring changes to individual `useQuery` calls.
27+
28+
```typescript
29+
import { createClient, fetchExchange } from 'urql';
30+
import Appsignal from '@appsignal/javascript';
31+
import { createAppsignalExchange } from '@appsignal/urql';
32+
33+
const appsignal = new Appsignal({
34+
key: 'YOUR FRONTEND API KEY'
35+
});
36+
37+
const client = createClient({
38+
url: 'https://api.example.com/graphql',
39+
exchanges: [createAppsignalExchange(appsignal), fetchExchange]
40+
});
41+
```
42+
43+
The exchange will automatically:
44+
- Report all GraphQL errors to AppSignal
45+
- Include the GraphQL query body as a parameter (visible in AppSignal's error details)
46+
- Include the endpoint URL as a tag
47+
- Include operation name and type as tags (when available)
48+
49+
### Error Details
50+
51+
When a GraphQL error occurs, AppSignal will receive:
52+
53+
- **Error message**: A concatenation of all GraphQL error messages
54+
- **Tags**:
55+
- `endpoint`: The GraphQL endpoint URL
56+
- `operationName`: The name of the GraphQL operation (if specified)
57+
- `operationType`: The type of operation (query, mutation, subscription)
58+
- **Parameters**:
59+
- `query`: The full GraphQL query body
60+
61+
This provides complete context for debugging GraphQL errors in your application.
62+
63+
## Development
64+
65+
### Installation
66+
67+
Make sure mono is installed and bootstrapped, see the [project README's development section](../../README.md#dev-install) for more information.
68+
69+
You can then run the following to start the compiler in _watch_ mode. This automatically compiles both the ES Module and CommonJS variants:
70+
71+
```bash
72+
yarn build:watch
73+
```
74+
75+
You can also build the library without watching the directory:
76+
77+
```
78+
yarn build # build both CJS and ESM
79+
yarn build:cjs # just CJS
80+
yarn build:esm # just ESM
81+
```
82+
83+
### Testing
84+
85+
The tests for this library use [Jest](https://jestjs.io) as the test runner. Once you've installed the dependencies, you can run the following command in the root of this repository to run the tests for all packages, or in the directory of a package to run only the tests pertaining to that package:
86+
87+
```bash
88+
yarn test
89+
```
90+
91+
### Versioning
92+
93+
This repo uses [Semantic Versioning][semver] (often referred to as _semver_). Each package in the repository is versioned independently from one another.
94+
95+
## Contributing
96+
97+
Thinking of contributing to this repo? Awesome! 🚀
98+
99+
Please follow our [Contributing guide][contributing-guide] in our documentation and follow our [Code of Conduct][coc].
100+
101+
Also, we would be very happy to send you Stroopwafles. Have look at everyone we send a package to so far on our [Stroopwafles page][waffles-page].
102+
103+
## Support
104+
105+
[Contact us][contact] and speak directly with the engineers working on AppSignal. They will help you get set up, tweak your code and make sure you get the most out of using AppSignal.
106+
107+
[appsignal]: https://appsignal.com
108+
[appsignal-sign-up]: https://appsignal.com/users/sign_up
109+
[contact]: mailto:support@appsignal.com
110+
[coc]: https://docs.appsignal.com/appsignal/code-of-conduct.html
111+
[waffles-page]: https://appsignal.com/waffles
112+
[docs]: http://docs.appsignal.com
113+
[contributing-guide]: http://docs.appsignal.com/appsignal/contributing.html
114+
115+
[semver]: http://semver.org/

packages/urql/jest.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
preset: "ts-jest",
3+
testEnvironment: "jsdom",
4+
roots: ["<rootDir>/src"],
5+
transform: {
6+
"^.+\\.tsx?$": "ts-jest"
7+
}
8+
}

packages/urql/package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "@appsignal/urql",
3+
"version": "1.0.0",
4+
"main": "dist/cjs/index.js",
5+
"module": "dist/esm/index.js",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/appsignal/appsignal-javascript.git",
9+
"directory": "packages/urql"
10+
},
11+
"license": "MIT",
12+
"scripts": {
13+
"build": "npm run build:cjs && npm run build:esm",
14+
"build:esm": "tsc -p tsconfig.esm.json",
15+
"build:esm:watch": "tsc -p tsconfig.esm.json -w --preserveWatchOutput",
16+
"build:cjs": "tsc -p tsconfig.cjs.json",
17+
"build:cjs:watch": "tsc -p tsconfig.cjs.json -w --preserveWatchOutput",
18+
"build:watch": "run-p build:cjs:watch build:esm:watch",
19+
"clean": "rimraf dist coverage",
20+
"link:npm": "npm link",
21+
"test": "jest --passWithNoTests",
22+
"test:watch": "jest --watch"
23+
},
24+
"dependencies": {
25+
"@appsignal/javascript": "=1.6.1"
26+
},
27+
"peerDependencies": {
28+
"urql": ">= 2.0.0 < 5",
29+
"wonka": ">= 4.0.0 < 7"
30+
},
31+
"publishConfig": {
32+
"access": "public"
33+
}
34+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { reportGraphQLError } from "../index"
2+
3+
describe("reportGraphQLError", () => {
4+
it("reports a GraphQL error to AppSignal with all metadata", () => {
5+
const { appsignal, span } = createMockAppsignal()
6+
const client = { url: "https://example.com/graphql" }
7+
const result = {
8+
error: {
9+
graphQLErrors: [{ message: "Not found" }]
10+
},
11+
operation: {
12+
query: { loc: { source: { body: "query { post { id } }" } } },
13+
kind: "query"
14+
}
15+
}
16+
17+
reportGraphQLError(result, appsignal, client)
18+
19+
expect(appsignal.sendError).toHaveBeenCalledWith(
20+
expect.objectContaining({
21+
name: "GraphQLError",
22+
message: "GraphQL Error: Not found"
23+
}),
24+
expect.any(Function)
25+
)
26+
expect(span.setTags).toHaveBeenCalledWith({
27+
endpoint: "https://example.com/graphql"
28+
})
29+
expect(span.setTags).toHaveBeenCalledWith({ operationType: "query" })
30+
expect(span.setParams).toHaveBeenCalledWith({
31+
query: "query { post { id } }"
32+
})
33+
})
34+
35+
it("sets endpoint tag", () => {
36+
const { appsignal, span } = createMockAppsignal()
37+
const result = {
38+
error: { message: "Something went wrong" },
39+
operation: {
40+
query: { loc: { source: { body: "mutation { createUser { id } }" } } }
41+
}
42+
}
43+
44+
const client = { url: "https://example.com/graphql" }
45+
reportGraphQLError(result, appsignal, client)
46+
47+
expect(span.setTags).toHaveBeenCalledWith({
48+
endpoint: "https://example.com/graphql"
49+
})
50+
})
51+
52+
it("sets query params on the span", () => {
53+
const { appsignal, span } = createMockAppsignal()
54+
const result = {
55+
error: { message: "Something went wrong" },
56+
operation: {
57+
query: { loc: { source: { body: "mutation { createPost { id } }" } } }
58+
}
59+
}
60+
61+
reportGraphQLError(result, appsignal, {})
62+
63+
expect(span.setParams).toHaveBeenCalledWith({
64+
query: "mutation { createPost { id } }"
65+
})
66+
})
67+
68+
it("sets operationType tag on the span", () => {
69+
const { appsignal, span } = createMockAppsignal()
70+
const result = {
71+
error: { message: "Something went wrong" },
72+
operation: { kind: "mutation" }
73+
}
74+
75+
reportGraphQLError(result, appsignal, {})
76+
77+
expect(span.setTags).toHaveBeenCalledWith({
78+
operationType: "mutation"
79+
})
80+
})
81+
})
82+
83+
function createMockAppsignal() {
84+
const span = {
85+
setTags: jest.fn(),
86+
setParams: jest.fn()
87+
}
88+
const appsignal = {
89+
sendError: jest.fn((_error: any, callback: any) => callback(span))
90+
}
91+
return { appsignal, span }
92+
}

0 commit comments

Comments
 (0)