Skip to content

Commit

Permalink
feat: allow custom serialize/deserialize redis messages (#3037)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum authored Oct 8, 2023
1 parent 0f1483c commit 8d143e4
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-rabbits-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-yoga/redis-event-target': minor
---

Added `serializeMessage` and `deserializeMessage` options to `createRedisEventTarget`
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Redis from 'ioredis-mock';
import { CustomEvent } from '@whatwg-node/events';
import { createRedisEventTarget } from '../src';

describe('createRedisEventTarget: serializer arg', () => {
it('uses native JSON by default', done => {
const eventTarget = createRedisEventTarget({
publishClient: new Redis({}),
subscribeClient: new Redis({}),
});

eventTarget.addEventListener('a', (event: CustomEvent) => {
// Serialized by JSON
expect(event.detail).toEqual({
someNumber: 1,
someBoolean: true,
someText: 'hi',
});
done();
});

const event = new CustomEvent('a', {
detail: {
someNumber: 1,
someBoolean: true,
someText: 'hi',
},
});
eventTarget.dispatchEvent(event);
});

it('can use a custom serializer', done => {
const eventTarget = createRedisEventTarget({
publishClient: new Redis({}),
subscribeClient: new Redis({}),
serializer: {
stringify: message => `__CUSTOM__${JSON.stringify(message)}`,
parse: (message: string) => {
const result = JSON.parse(message.replace(/^__CUSTOM__/, ''));
for (const key in result) {
if (typeof result[key] === 'number') {
result[key]++;
}
}
return result;
},
},
});

eventTarget.addEventListener('b', (event: CustomEvent) => {
expect(event.detail).toEqual({
someNumber: 2,
someBoolean: true,
someText: 'hi',
});
done();
});

const event = new CustomEvent('b', {
detail: {
someNumber: 1,
someBoolean: true,
someText: 'hi',
},
});
eventTarget.dispatchEvent(event);
});
});
10 changes: 8 additions & 2 deletions packages/event-target/redis-event-target/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import { CustomEvent } from '@whatwg-node/events';
export type CreateRedisEventTargetArgs = {
publishClient: Redis | Cluster;
subscribeClient: Redis | Cluster;
serializer?: {
stringify: (message: unknown) => string;
parse: (message: string) => unknown;
};
};

export function createRedisEventTarget<TEvent extends CustomEvent>(
args: CreateRedisEventTargetArgs,
): TypedEventTarget<TEvent> {
const { publishClient, subscribeClient } = args;

const serializer = args.serializer ?? JSON;

const callbacksForTopic = new Map<string, Set<(event: TEvent) => void>>();

function onMessage(channel: string, message: string) {
Expand All @@ -21,7 +27,7 @@ export function createRedisEventTarget<TEvent extends CustomEvent>(
}

const event = new CustomEvent(channel, {
detail: message === '' ? null : JSON.parse(message),
detail: message === '' ? null : serializer.parse(message),
}) as TEvent;
for (const callback of callbacks) {
callback(event);
Expand Down Expand Up @@ -65,7 +71,7 @@ export function createRedisEventTarget<TEvent extends CustomEvent>(
dispatchEvent(event: TEvent) {
publishClient.publish(
event.type,
event.detail === undefined ? '' : JSON.stringify(event.detail),
event.detail === undefined ? '' : serializer.stringify(event.detail),
);
return true;
},
Expand Down
34 changes: 34 additions & 0 deletions website/src/pages/docs/features/subscriptions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,40 @@ suitable solution for serverless or edge function environments.
data structures over the wire you can use tools such as
[`superjson`](https://github.com/blitz-js/superjson).

### Custom serializer

By default, messages will be serialized to Redis using the native `JSON` available in your
JavaScript environment. However, you can customize this by providing an alternative with the
`serializer` option that expects a `stringify` and a `parse` methods similar to the native `JSON`.
For example, you can install the [superjson](https://github.com/blitz-js/superjson) package and use
it in the Redis event target instead:

```ts
import SuperJSON from 'superjson'

const publishClient = new Redis()
const subscribeClient = new Redis()

const eventTarget = createRedisEventTarget({
publishClient,
subscribeClient,
serializer: SuperJSON
})
```

You can also provide your own logic if you want:

```ts
const eventTarget = createRedisEventTarget({
publishClient,
subscribeClient,
serializer: {
stringify: data => 'some serialized data',
parse: message => ({ some: 'deserialized data' })
}
})
```

## Advanced

### Filter and Map Values
Expand Down

0 comments on commit 8d143e4

Please sign in to comment.