Clients

TypeScript client

This is the base implementation of the WunderGraph HTTP protocol in TypeScript that can be used on both browser and server environments. It's used as the base interface for the Web client, React and Next.js implementations.

Configuration

Let's start by adding the WunderGraph TypeScript client generator to your project.

1
2
3
4
5
6
7
8
9
10
11
// wundergraph.config.ts
configureWunderGraphApplication({
// ... your configuration
codeGenerators: [
{
templates: [templates.typescript.client],
path: '../generated',
},
],
});

Create the client

The generated createClient will return a fully typesafe client that can be used to execute operations.

1
2
3
import { createClient } from 'generated/client';
const client = createClient();

Client configuration

Custom baseURL

The client can be configured with a custom baseURL.

1
2
3
const client = createClient({
baseURL: 'https://my-custom-base-url.com',
});

Node.js support

You can use the client on server environments that don't have a build-in fetch implementation by using the customFetch configuration option.

Install node-fetch or any other fetch polyfill.

1
npm i node-fetch

And add it to the client configuration.

1
2
3
4
5
import fetch from 'node-fetch';
const client = createClient({
customFetch: fetch,
});

Browser

If you target older browsers you will need a polyfill for fetch, AbortController, AbortSignal and possibly Promise.

1
2
3
4
5
6
7
import 'promise-polyfill/src/polyfill';
import 'yet-another-abortcontroller-polyfill';
import { fetch } from 'whatwg-fetch';
const client = createClient({
customFetch: fetch,
});

Adding custom headers

1
2
3
4
5
6
7
8
9
10
11
const client = createClient({
extraHeaders: {
customHeader: 'value',
},
});
// or
client.setExtraHeaders({
customHeader: 'value',
});

Methods

Run a query

1
2
3
4
5
6
const response = await client.query({
operationName: 'Hello',
input: {
hello: 'World',
},
});

Mutation

1
2
3
4
5
6
const response = await client.mutate({
operationName: 'SetName',
input: {
name: 'WunderGraph',
},
});

LiveQuery

1
2
3
4
5
6
7
8
9
10
client.subscribe(
{
operationName: 'Hello',
input: {
name: 'World',
},
liveQuery: true,
},
(response) => {}
);

Subscription

1
2
3
4
5
6
7
8
9
client.subscribe(
{
operationName: 'Countdown',
input: {
from: 100,
},
},
(response) => {}
);

One-of subscription

You can run subscriptions using subscribeOnce, this will return the subscription response directly and will not setup a stream. This is useful for SSR purposes for example.

1
2
3
4
5
6
7
8
9
10
const response = await client.subscribe(
{
operationName: 'Countdown',
input: {
from: 100,
},
subscribeOnce: true,
},
(response) => {}
);

Upload files

1
2
3
4
const { fileKeys } = await client.uploadFiles({
provider: S3Provider.minio,
files,
});

Auth

Login

1
client.login('github');

Log out

1
2
3
client.logout({
logoutOpenidConnectProvider: true,
});

Fetch user

1
const user = await client.fetchUser();

AbortController

Almost all methods accept an AbortController instance that can be used to cancel the request.

1
2
3
4
5
6
7
8
9
10
const controller = new AbortController();
const { fileKeys } = await client.uploadFiles({
abortSignal: abortController.signal,
provider: S3Provider.minio,
files,
});
// cancel the request
controller.abort();

Error handling (ClientResponseError)

Operations

The errors that can be returned from an operation are one of ResponseError, InputValidationError, or GraphQLResponseError. These three errors compose the type union named ClientResponseError.

Network errors and non 2xx responses are returned as a ResponseError or InputValidationError object and contain the status code through the statusCode property. You can be sure that the request was successful if the method doesn't throw an error.

GraphQLResponseError

Query and mutation errors are returned as a GraphQLResponseError object. By default, the first error specifies the error message, but you can access all GraphQL errors through the errors property:

1
2
3
4
5
6
7
8
9
10
11
12
const { data, error } = await client.query({
operationName: 'Hello',
input: {
hello: 'World',
},
});
if (error instanceof GraphQLResponseError) {
error.errors[0].location;
} else if (error instanceof ResponseError) {
error.statusCode;
}

ResponseError

Methods that initiate a network request throw a ResponseError if the request fails to initiate or the response is not 2xx. Note that ResponseError returns a single error and does not have an errors property.

InputValidationError

If the source of failure was due to an invalid input, the error received will be an InputValdationError. By default, the first error specifies the error message, but you can access all input validation errors through the errors property.

The errors property is an array of errors with the following structure:

1
2
3
4
5
type error = {
propertyPath: string; // provides the path to the error (useful if the error nested)
message: string; // the error
invalidValue: any; // the invalid input
};
1
2
3
4
5
6
7
8
9
10
11
import { InputValidationError } from './InputValidationError';
const { data, error } = await client.query({
operationName: 'Hello',
input: {}, // a required input is missing
});
if (error instanceof InputValidationError) {
error.message; // the top level error
error.errors; // an array of errors
}

Limitations

  • Subscriptions are not supported server side, but can be fetched using subscribeOnce.
Previous
Overview

Was this article helpful to you?
Provide feedback

Edit this page