-
-
Notifications
You must be signed in to change notification settings - Fork 36
feat(server): New Instruments API #2068
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
aed1821
6de524b
f2908fb
eaffa1e
5a8065c
9627f0b
07826f7
695a2ba
229f8b7
ee7c3b8
2f22410
319c9d4
36012c9
fc4cd52
68350f4
1796ef3
c7d8041
a96b225
04ffb29
84e9572
86f6ed6
c519812
bd9cf2e
cac420c
0dac2e5
9e59299
72a41e0
5149d67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@whatwg-node/server": patch | ||
--- | ||
dependencies updates: | ||
- Updated dependency [`@whatwg-node/promise-helpers@^1.2.2` ↗︎](https://www.npmjs.com/package/@whatwg-node/promise-helpers/v/1.2.2) (from `^1.0.0`, in `dependencies`) | ||
- Added dependency [`@envelop/[email protected]` ↗︎](https://www.npmjs.com/package/@envelop/instruments/v/1.0.0) (to `dependencies`) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
--- | ||
'@whatwg-node/server': minor | ||
--- | ||
|
||
Add new Instruments API | ||
|
||
Introduction of a new API allowing to instrument the graphql pipeline. | ||
|
||
This new API differs from already existing Hooks by not having access to input/output of phases. The | ||
goal of `Instruments` is to run allow running code before, after or around the **whole process of a | ||
phase**, including plugins hooks executions. | ||
|
||
The main use case of this new API is observability (monitoring, tracing, etc...). | ||
|
||
### Basic usage | ||
|
||
```ts | ||
import Sentry from '@sentry/node' | ||
import { createServerAdapter } from '@whatwg-node/server' | ||
|
||
const server = createServerAdapter( | ||
(req, res) => { | ||
//... | ||
}, | ||
{ | ||
plugins: [ | ||
{ | ||
instruments: { | ||
request: ({ request }, wrapped) => | ||
Sentry.startSpan({ name: 'Graphql Operation' }, async () => { | ||
try { | ||
await wrapped() | ||
} catch (err) { | ||
Sentry.captureException(err) | ||
} | ||
}) | ||
} | ||
} | ||
] | ||
} | ||
) | ||
``` | ||
|
||
### Multiple instruments plugins | ||
|
||
It is possible to have multiple instruments plugins (Prometheus and Sentry for example), they will | ||
be automatically composed by envelop in the same order than the plugin array (first is outermost, | ||
last is inner most). | ||
|
||
```ts | ||
import { createServerAdapter } from '@whatwg-node/server' | ||
|
||
const server = createServerAdapter( | ||
(req, res) => { | ||
//... | ||
}, | ||
{ plugins: [useSentry(), useOpentelemetry()] } | ||
) | ||
``` | ||
|
||
```mermaid | ||
sequenceDiagram | ||
Sentry->>Opentelemetry: ; | ||
Opentelemetry->>Server Adapter: ; | ||
Server Adapter->>Opentelemetry: ; | ||
Opentelemetry->>Sentry: ; | ||
``` | ||
|
||
### Custom instruments ordering | ||
|
||
If the default composition ordering doesn't suite your need, you can manually compose instruments. | ||
This allows to have a different execution order of hooks and instruments. | ||
|
||
```ts | ||
import { composeInstruments, createServerAdapter } from '@whatwg-node/server' | ||
|
||
const { instruments: sentryInstruments, ...sentryPlugin } = useSentry() | ||
const { instruments: otelInstruments, ...otelPlugin } = useOpentelemetry() | ||
const instruments = composeInstruments([otelInstruments, sentryInstruments]) | ||
|
||
const server = createServerAdapter( | ||
(req, res) => { | ||
//... | ||
}, | ||
{ plugins: [{ instruments }, sentryPlugin, otelPlugin] } | ||
) | ||
``` | ||
|
||
```mermaid | ||
sequenceDiagram | ||
Opentelemetry->>Sentry: ; | ||
Sentry->>Server Adapter: ; | ||
Server Adapter->>Sentry: ; | ||
Sentry->>Opentelemetry: ; | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@whatwg-node/promise-helpers': patch | ||
--- | ||
|
||
Fix return type of the callback of `iterateAsync`. The callback can actually return `null` or | ||
`undefined`, the implementation is already handling this case. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,3 +78,6 @@ package-lock.json | |
eslint_report.json | ||
|
||
deno.lock | ||
.helix/config.toml | ||
.helix/languages.toml | ||
.mise.toml |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,6 @@ | ||
{ | ||
"imports": { | ||
"@jest/globals": "./deno-jest.ts", | ||
"@whatwg-node/cookie-store": "./packages/cookie-store/src/index.ts", | ||
"@whatwg-node/disposablestack": "./packages/disposablestack/src/index.ts", | ||
"@whatwg-node/fetch": "./packages/fetch/dist/esm-ponyfill.js", | ||
"@whatwg-node/events": "./packages/events/src/index.ts", | ||
"fetchache": "./packages/fetchache/src/index.ts", | ||
"@whatwg-node/node-fetch": "./packages/node-fetch/src/index.ts", | ||
"@whatwg-node/server": "./packages/server/src/index.ts", | ||
"@whatwg-node/server-plugin-cookies": "./packages/server-plugin-cookies/src/index.ts", | ||
"@whatwg-node/promise-helpers": "./packages/promise-helpers/src/index.ts" | ||
"@whatwg-node/fetch": "./packages/fetch/dist/esm-ponyfill.js" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,33 @@ | ||||||
import { describe, expect, it } from '@jest/globals'; | ||||||
import { createServerAdapter, ServerAdapterPlugin } from '@whatwg-node/server'; | ||||||
|
||||||
describe('instruments', () => { | ||||||
it('should wrap request handler with instruments and automatically compose them', async () => { | ||||||
const results: string[] = []; | ||||||
|
||||||
function make(name: string): ServerAdapterPlugin { | ||||||
return { | ||||||
instruments: { | ||||||
request: async (_, wrapped) => { | ||||||
results.push(`pre-${name}`); | ||||||
await wrapped(); | ||||||
results.push(`post-${name}`); | ||||||
}, | ||||||
}, | ||||||
}; | ||||||
} | ||||||
|
||||||
const adapter = createServerAdapter<{}>( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Replace empty object type with a more specific type. Using -const adapter = createServerAdapter<{}>(
+const adapter = createServerAdapter<unknown>( Alternative solutions depending on the actual requirements: // If you need to specify an empty object type:
const adapter = createServerAdapter<Record<string, never>>(
// Or if there's a specific context type that should be used:
const adapter = createServerAdapter<YourContextType>( 🧰 Tools🪛 Biome (1.9.4)[error] 20-20: Don't use '{}' as a type. Prefer explicitly define the object shape. '{}' means "any non-nullable value". (lint/complexity/noBannedTypes) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Replace empty object type with a more specific type Using - const adapter = createServerAdapter<{}>(
+ const adapter = createServerAdapter<Record<string, never>>( Alternatively, if there's a specific context type that should be used here, consider using that instead. 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 20-20: Don't use '{}' as a type. Prefer explicitly define the object shape. '{}' means "any non-nullable value". (lint/complexity/noBannedTypes) |
||||||
() => { | ||||||
results.push('request'); | ||||||
return Response.json({ message: 'Hello, World!' }); | ||||||
}, | ||||||
{ | ||||||
plugins: [make('1'), make('2'), make('3')], | ||||||
}, | ||||||
); | ||||||
|
||||||
await adapter.fetch('http://whatwg-node/graphql'); | ||||||
expect(results).toEqual(['pre-1', 'pre-2', 'pre-3', 'request', 'post-3', 'post-2', 'post-1']); | ||||||
}); | ||||||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1235,6 +1235,14 @@ | |
dependencies: | ||
tslib "^2.4.0" | ||
|
||
"@envelop/[email protected]": | ||
version "1.0.0" | ||
resolved "https://registry.yarnpkg.com/@envelop/instruments/-/instruments-1.0.0.tgz#7e36926b6212048258ce1939bcf5a52e2a5dbe4d" | ||
integrity sha512-f4lHoti7QgUIluIGTM0mG9Wf9/w6zc1mosYmyFkrApeHSP2PIUC6a8fMoqkdk6pgVOps39kLdIhOPF8pIKS8/A== | ||
dependencies: | ||
"@whatwg-node/promise-helpers" "^1.2.1" | ||
tslib "^2.5.0" | ||
|
||
"@esbuild-plugins/[email protected]": | ||
version "0.2.3" | ||
resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz#0e4497a2b53c9e9485e149bc92ddb228438d6bcf" | ||
|
@@ -7700,7 +7708,6 @@ mvdan-sh@^0.10.1: | |
|
||
"nan@github:JCMais/nan#fix/electron-failures": | ||
version "2.22.0" | ||
uid "0ec2eca8b2fd7518affb3945d087e393ad839b7e" | ||
resolved "https://codeload.github.com/JCMais/nan/tar.gz/0ec2eca8b2fd7518affb3945d087e393ad839b7e" | ||
|
||
nanoid@^3.3.6: | ||
|
@@ -9767,7 +9774,7 @@ [email protected]: | |
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" | ||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== | ||
|
||
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2, tslib@^2.6.3, tslib@^2.8.0: | ||
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.2, tslib@^2.6.3, tslib@^2.8.0: | ||
version "2.8.1" | ||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" | ||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== | ||
|
@@ -9873,7 +9880,6 @@ [email protected]: | |
|
||
uWebSockets.js@uNetworking/uWebSockets.js#v20.51.0: | ||
version "20.51.0" | ||
uid "6609a88ffa9a16ac5158046761356ce03250a0df" | ||
resolved "https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/6609a88ffa9a16ac5158046761356ce03250a0df" | ||
|
||
ufo@^1.5.4: | ||
|
Uh oh!
There was an error while loading. Please reload this page.