Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions packages/urql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# `@appsignal/urql`

- [AppSignal.com website][appsignal]
- [Documentation][docs]
- [Support][contact]

The `@appsignal/javascript` integration for urql GraphQL client.

See also the [mono repo README](../../README.md) for more information.

## Installation

Add the `@appsignal/urql` and `@appsignal/javascript` packages to your `package.json`. Then, run `yarn install`/`npm install`.

You can also add these packages to your `package.json` on the command line:

```
yarn add @appsignal/javascript @appsignal/urql urql wonka
npm install --save @appsignal/javascript @appsignal/urql urql wonka
```

## Usage

### Urql Exchange

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.

```typescript
import { createClient, fetchExchange } from 'urql';
import Appsignal from '@appsignal/javascript';
import { createAppsignalExchange } from '@appsignal/urql';

const appsignal = new Appsignal({
key: 'YOUR FRONTEND API KEY'
});

const client = createClient({
url: 'https://api.example.com/graphql',
exchanges: [createAppsignalExchange(appsignal), fetchExchange]
});
```

The exchange will automatically:
- Report all GraphQL errors to AppSignal
- Include the GraphQL query body as a parameter (visible in AppSignal's error details)
- Include the endpoint URL as a tag
- Include operation name and type as tags (when available)

### Error Details

When a GraphQL error occurs, AppSignal will receive:

- **Error message**: A concatenation of all GraphQL error messages
- **Tags**:
- `endpoint`: The GraphQL endpoint URL
- `operationName`: The name of the GraphQL operation (if specified)
- `operationType`: The type of operation (query, mutation, subscription)
- **Parameters**:
- `query`: The full GraphQL query body

This provides complete context for debugging GraphQL errors in your application.

## Development

### Installation

Make sure mono is installed and bootstrapped, see the [project README's development section](../../README.md#dev-install) for more information.

You can then run the following to start the compiler in _watch_ mode. This automatically compiles both the ES Module and CommonJS variants:

```bash
yarn build:watch
```

You can also build the library without watching the directory:

```
yarn build # build both CJS and ESM
yarn build:cjs # just CJS
yarn build:esm # just ESM
```

### Testing

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:

```bash
yarn test
```

### Versioning

This repo uses [Semantic Versioning][semver] (often referred to as _semver_). Each package in the repository is versioned independently from one another.

## Contributing

Thinking of contributing to this repo? Awesome! 🚀

Please follow our [Contributing guide][contributing-guide] in our documentation and follow our [Code of Conduct][coc].

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

## Support

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

[appsignal]: https://appsignal.com
[appsignal-sign-up]: https://appsignal.com/users/sign_up
[contact]: mailto:support@appsignal.com
[coc]: https://docs.appsignal.com/appsignal/code-of-conduct.html
[waffles-page]: https://appsignal.com/waffles
[docs]: http://docs.appsignal.com
[contributing-guide]: http://docs.appsignal.com/appsignal/contributing.html

[semver]: http://semver.org/
8 changes: 8 additions & 0 deletions packages/urql/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "jsdom",
roots: ["<rootDir>/src"],
transform: {
"^.+\\.tsx?$": "ts-jest"
}
}
34 changes: 34 additions & 0 deletions packages/urql/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@appsignal/urql",
"version": "1.0.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"repository": {
"type": "git",
"url": "https://github.com/appsignal/appsignal-javascript.git",
"directory": "packages/urql"
},
"license": "MIT",
"scripts": {
"build": "npm run build:cjs && npm run build:esm",
"build:esm": "tsc -p tsconfig.esm.json",
"build:esm:watch": "tsc -p tsconfig.esm.json -w --preserveWatchOutput",
"build:cjs": "tsc -p tsconfig.cjs.json",
"build:cjs:watch": "tsc -p tsconfig.cjs.json -w --preserveWatchOutput",
"build:watch": "run-p build:cjs:watch build:esm:watch",
"clean": "rimraf dist coverage",
"link:npm": "npm link",
"test": "jest --passWithNoTests",
"test:watch": "jest --watch"
},
"dependencies": {
"@appsignal/javascript": "=1.6.1"
},
"peerDependencies": {
"urql": ">= 2.0.0 < 5",
"wonka": ">= 4.0.0 < 7"
},
"publishConfig": {
"access": "public"
}
}
68 changes: 68 additions & 0 deletions packages/urql/src/index.ts
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add tests for this as well.

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { pipe, tap } from 'wonka';
import type Appsignal from '@appsignal/javascript';

/**
* Custom urql exchange that automatically reports GraphQL errors to AppSignal.
*
* This exchange intercepts all query/mutation results and reports any errors
* to AppSignal without requiring changes to individual useQuery calls.
*
* @example
* ```typescript
* import { createClient, fetchExchange } from 'urql';
* import Appsignal from '@appsignal/javascript';
* import { createAppsignalExchange } from '@appsignal/urql';
*
* const appsignal = new Appsignal({
* key: 'YOUR FRONTEND API KEY'
* });
*
* const client = createClient({
* url: 'https://api.example.com/graphql',
* exchanges: [createAppsignalExchange(appsignal), fetchExchange]
* });
* ```
*/
export const createAppsignalExchange = (appsignal: Appsignal) => ({ forward, client }: any) => (ops$: any) => {
return pipe(
forward(ops$),
tap((result: any) => {
if (result.error) {
const { error, operation } = result;

// Convert CombinedError to a proper Error with meaningful message
const errorMessage = error.graphQLErrors?.length > 0
? error.graphQLErrors.map((e: any) => e.message).join(', ')
: error.message;

const reportError = new Error(`GraphQL Error: ${errorMessage}`);
reportError.name = 'GraphQLError';
(reportError as any).stack = error.stack || reportError.stack;

// Send error to AppSignal with metadata
appsignal.sendError(reportError, (span) => {
// Add endpoint URL as a tag
if (client?.url) {
span.setTags({ endpoint: client.url });
}

// Add GraphQL query body as a param
if (operation?.query) {
const queryBody = operation.query.loc?.source?.body;
if (queryBody) {
span.setParams({ query: queryBody });
}
}

// Add operation metadata
if (operation?.operationName) {
span.setTags({ operationName: operation.operationName });
}
if (operation?.kind) {
span.setTags({ operationType: operation.kind });
}
});
}
})
);
};
7 changes: 7 additions & 0 deletions packages/urql/tsconfig.cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist/cjs",
"module": "commonjs"
}
}
7 changes: 7 additions & 0 deletions packages/urql/tsconfig.esm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist/esm",
"module": "es6"
}
}
12 changes: 12 additions & 0 deletions packages/urql/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*"],
"exclude": [
"src/**/__tests__",
"src/**/__mocks__"
],
"compilerOptions": {
"rootDir": "./src",
"target": "es5"
}
}
Loading