Skip to content

Commit

Permalink
Add logging and request retry logic, timeouts, hooks with ky backend (
Browse files Browse the repository at this point in the history
  • Loading branch information
psirenny authored Jan 29, 2025
1 parent 5204421 commit 8ac0b2f
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/calm-countries-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@spear-ai/relay-environment": major
---

Requests now throw an `HTTPError` when they timeout or when they return a non-200 status code.
5 changes: 5 additions & 0 deletions .changeset/cyan-spoons-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@spear-ai/relay-environment": minor
---

Added debug logging and the ability to pass in a custom logger.
5 changes: 5 additions & 0 deletions .changeset/short-eagles-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@spear-ai/relay-environment": minor
---

Backed requests with the [ky](https://github.com/sindresorhus/ky) library which adds support for custom hooks. This makes it easier to integrate authentication workflows.
5 changes: 5 additions & 0 deletions .changeset/short-fans-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@spear-ai/relay-environment": minor
---

Added request retries and timeouts.
7 changes: 6 additions & 1 deletion packages/relay-environment/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
"url": "https://spear.ai"
},
"type": "module",
"dependencies": {
"abslog": "^2.4.4",
"ky": "^1.7.4",
"uuid": "^11.0.5"
},
"devDependencies": {
"@spear-ai/eslint-config": "20.0.1",
"@spear-ai/npm-package-json-lint-config": "3.1.1",
Expand All @@ -30,7 +35,7 @@
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"peerDependencies": {
"relay-runtime": "^16.2.0"
"relay-runtime": "^18.2.0"
},
"repository": {
"type": "git",
Expand Down
123 changes: 82 additions & 41 deletions packages/relay-environment/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import abslog, { AbstractLoggerOptions, ValidLogger } from "abslog";
import ky, { Hooks, Options as KyOptions } from "ky";
import {
CacheConfig,
Environment,
Expand All @@ -9,35 +11,54 @@ import {
Store,
Variables,
} from "relay-runtime";
import { v4 as uuidv4 } from "uuid";

const networkFetch = async (
request: RequestParameters,
variables: Variables,
apiUrl: string,
): Promise<GraphQLResponse> => {
const response = await fetch(apiUrl, {
body: JSON.stringify({
query: request.text,
variables,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
method: "POST",
});
type PartialKyOptions = Pick<KyOptions, "hooks" | "retry" | "timeout">;

return (await response.json()) as unknown as GraphQLResponse;
};
const makeFetchResponse = (
options: PartialKyOptions & {
apiUrl: string;
cacheSize: number;
cacheTtl: number;
latencyRetentionCount: number;
logger: ValidLogger;
recentFetchLatencyList: number[];
},
) => {
const {
apiUrl,
cacheSize,
cacheTtl,
hooks,
latencyRetentionCount,
logger,
recentFetchLatencyList,
...kyOptions
} = options;

const createFetchResponse = (options: {
apiUrl: string;
cacheSize: number;
cacheTtl: number;
latencyRetentionCount: number;
recentFetchLatencyList: number[];
}) => {
const { apiUrl, cacheSize, cacheTtl, latencyRetentionCount, recentFetchLatencyList } = options;
const requestId = uuidv4();

const mergedHooks: Hooks = {
...hooks,
afterResponse: [
...(hooks?.afterResponse ?? []),
(request, requestOptions, response) => {
logger.debug({ request, requestId, requestOptions, response }, "afterResponse");
},
],
beforeRequest: [
...(hooks?.beforeRequest ?? []),
(request, requestOptions) => {
logger.debug({ request, requestId, requestOptions }, "beforeRequest");
},
],
beforeRetry: [
...(hooks?.beforeRetry ?? []),
(retryOptions) => {
logger.debug({ requestId, retryOptions }, "beforeRetry");
},
],
};

const responseCache: QueryResponseCache = new QueryResponseCache({
size: cacheSize,
Expand All @@ -59,7 +80,15 @@ const createFetchResponse = (options: {
}

const fetchStart = performance.now();
const fetchResponse = await networkFetch(parameters, variables, apiUrl);
const fetchResponse = await ky.post<GraphQLResponse>(apiUrl, {
...kyOptions,
hooks: mergedHooks,
json: {
query: parameters.text,
variables,
},
});
const response = await fetchResponse.json();
const fetchEnd = performance.now();

recentFetchLatencyList.push(fetchEnd - fetchStart);
Expand All @@ -68,7 +97,7 @@ const createFetchResponse = (options: {
recentFetchLatencyList.shift();
}

return fetchResponse;
return response;
};
};

Expand All @@ -79,28 +108,40 @@ export type RelayEnvironment = Environment & {
};
};

export const createEnvironment = (options: {
/** The GraphQL API URL. */
apiUrl: string;
/** The maximum number of entities to keep inside Relay’s cache. */
cacheSize?: number | undefined;
/** The maximum time to keep entities inside Relay’s cache. */
cacheTtl?: number | undefined;
/** The maximum number of latency metrics to retain. */
latencyRetentionCount?: number | undefined;
}): RelayEnvironment => {
const { apiUrl, cacheSize = 100, cacheTtl = 5000, latencyRetentionCount = 100 } = options;
export const createEnvironment = (
options: PartialKyOptions & {
/** The GraphQL API endpoint URL. */
apiUrl: string;
/** The maximum number of entities to keep inside Relay's cache. */
cacheSize?: number | undefined;
/** The maximum time to keep entities inside Relay's cache. */
cacheTtl?: number | undefined;
/** The maximum number of latency metrics to retain. */
latencyRetentionCount?: number | undefined;
/** The logger to use when handling requests. */
logger?: AbstractLoggerOptions | undefined;
},
): RelayEnvironment => {
const {
cacheSize = 100,
cacheTtl = 5000,
latencyRetentionCount = 100,
logger: customLogger,
...rest
} = options;
const recentFetchLatencyList: number[] = [];
const logger = abslog(customLogger);

return new Environment({
isServer: false,
network: Network.create(
createFetchResponse({
apiUrl,
makeFetchResponse({
cacheSize,
cacheTtl,
latencyRetentionCount,
logger,
recentFetchLatencyList,
...rest,
}),
),
options: { recentFetchLatencyList },
Expand Down
37 changes: 36 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6210,9 +6210,11 @@ __metadata:
"@spear-ai/prettier-config": "npm:2.2.0"
"@spear-ai/tsconfig": "npm:4.0.0"
"@types/relay-runtime": "npm:18.2.3"
abslog: "npm:^2.4.4"
autoprefixer: "npm:10.4.20"
eslint: "npm:8.57.1"
graphql: "npm:16.10.0"
ky: "npm:^1.7.4"
npm-package-json-lint: "npm:8.0.0"
prettier: "npm:3.4.2"
react: "npm:19.0.0"
Expand All @@ -6221,8 +6223,9 @@ __metadata:
tailwindcss: "npm:3.4.17"
tsup: "npm:8.3.5"
typescript: "npm:5.7.2"
uuid: "npm:^11.0.5"
peerDependencies:
relay-runtime: ^16.2.0
relay-runtime: ^18.2.0
languageName: unknown
linkType: soft

Expand Down Expand Up @@ -8155,6 +8158,15 @@ __metadata:
languageName: node
linkType: hard

"abslog@npm:^2.4.4":
version: 2.4.4
resolution: "abslog@npm:2.4.4"
dependencies:
nooplog: "npm:1.0.2"
checksum: 10/c1596bd07d1bbbbd6ba18d257c5dc6d88cc8c2b339e1b2a3d203a67de4480ff22fed18511b7927c6fc171becafc09fa1b0c2a904bd18af17d7b5d5e37fcbc2b3
languageName: node
linkType: hard

"accepts@npm:~1.3.5":
version: 1.3.8
resolution: "accepts@npm:1.3.8"
Expand Down Expand Up @@ -13499,6 +13511,13 @@ __metadata:
languageName: node
linkType: hard

"ky@npm:^1.7.4":
version: 1.7.4
resolution: "ky@npm:1.7.4"
checksum: 10/a5e98434866e4e8e691f76aaa4b4d5bd3abe6527dbb0e853ee37c4475852af6e0cbeef442be31b6824ca7fd3998740492419a45be60352836b9a21a54458f56c
languageName: node
linkType: hard

"language-subtag-registry@npm:^0.3.20":
version: 0.3.23
resolution: "language-subtag-registry@npm:0.3.23"
Expand Down Expand Up @@ -14493,6 +14512,13 @@ __metadata:
languageName: node
linkType: hard

"nooplog@npm:1.0.2":
version: 1.0.2
resolution: "nooplog@npm:1.0.2"
checksum: 10/ef22d1555d8ec15cea24b223cb3ede48543a9cc90ab73884d413e89c47b842a4e042172478ead252e2f8873fe3cb683694cd546474522b744ae525e5c62ad1eb
languageName: node
linkType: hard

"nopt@npm:^7.0.0":
version: 7.2.1
resolution: "nopt@npm:7.2.1"
Expand Down Expand Up @@ -18599,6 +18625,15 @@ __metadata:
languageName: node
linkType: hard

"uuid@npm:^11.0.5":
version: 11.0.5
resolution: "uuid@npm:11.0.5"
bin:
uuid: dist/esm/bin/uuid
checksum: 10/0594ecdff3051e15d4a2c614b4c72e73af373bde0a5d156512353c01156975295d024ae8d7151846d7bd4d22ccd251b16ed51b4318fa71505fb20ad984102dc1
languageName: node
linkType: hard

"uuid@npm:^9.0.0":
version: 9.0.1
resolution: "uuid@npm:9.0.1"
Expand Down

0 comments on commit 8ac0b2f

Please sign in to comment.