Skip to content

Commit 9bfb858

Browse files
authored
feat: support HTTP and Socket.io in the urql and relay examples (#937)
* feat: support HTTP and Socket.io in the urql and relay examples * feat: cover all todo example apps with integration tests on both SSE and Socket.io * remove patches * chore: add yoga example * chore: add yoga to list of ignored bob packages
1 parent 7b7778e commit 9bfb858

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1078
-878
lines changed

bob.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports = {
77
"@n1ru4l/todo-example-server",
88
"@n1ru4l/todo-example-server-helix",
99
"@n1ru4l/todo-example-server-ws",
10+
"@n1ru4l/todo-example-server-yoga",
1011
"@n1ru4l/end2end-tests",
1112
], // ignored packages
1213
base: "origin/main",

packages/socket-io-graphql-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ A layer for serving a GraphQL schema via a socket.io server. Supports Queries, M
66

77
**Note:** Use [`@n1ru4l/socket-io-graphql-client`](https://github.com/n1ru4l/graphql-live-queries/tree/main/packages/socket-io-graphql-client) for executing operations against the server.
88

9-
For a full setup check out the [todo-example-server](https://github.com/n1ru4l/graphql-live-queries/tree/main/packages/todo-example/server).
9+
For a full setup check out the [todo-example-server-socket-io](https://github.com/n1ru4l/graphql-live-queries/tree/main/packages/todo-example/server-socket-io).
1010

1111
## Install Instructions
1212

packages/todo-example/client-apollo/package.json

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"@types/classnames": "2.3.0",
2020
"@types/react-dom": "17.0.17",
2121
"@vitejs/plugin-react-refresh": "1.3.6",
22-
"graphiql": "1.4.8",
2322
"classnames": "2.3.1",
2423
"react": "17.0.2",
2524
"react-dom": "17.0.2",
@@ -33,46 +32,5 @@
3332
"generate:types": "graphql-codegen --config codegen.yml",
3433
"ts:check": "tsc --noEmit"
3534
},
36-
"type": "module",
37-
"main": "dist/cjs/index.js",
38-
"module": "dist/esm/index.js",
39-
"typings": "dist/typings/index.d.ts",
40-
"typescript": {
41-
"definition": "dist/typings/index.d.ts"
42-
},
43-
"exports": {
44-
".": {
45-
"require": {
46-
"types": "./dist/typings/index.d.ts",
47-
"default": "./dist/cjs/index.js"
48-
},
49-
"import": {
50-
"types": "./dist/typings/index.d.ts",
51-
"default": "./dist/esm/index.js"
52-
},
53-
"default": {
54-
"types": "./dist/typings/index.d.ts",
55-
"default": "./dist/esm/index.js"
56-
}
57-
},
58-
"./*": {
59-
"require": {
60-
"types": "./dist/typings/*.d.ts",
61-
"default": "./dist/cjs/*.js"
62-
},
63-
"import": {
64-
"types": "./dist/typings/*.d.ts",
65-
"default": "./dist/esm/*.js"
66-
},
67-
"default": {
68-
"types": "./dist/typings/*.d.ts",
69-
"default": "./dist/esm/*.js"
70-
}
71-
},
72-
"./package.json": "./package.json"
73-
},
74-
"publishConfig": {
75-
"directory": "dist",
76-
"access": "public"
77-
}
35+
"type": "commonjs"
7836
}

packages/todo-example/client-apollo/src/GraphiQLWidget.css

Lines changed: 0 additions & 3 deletions
This file was deleted.

packages/todo-example/client-apollo/src/GraphiQLWidget.tsx

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {
2+
ApolloLink,
3+
Operation,
4+
FetchResult,
5+
Observable,
6+
ApolloClient,
7+
InMemoryCache,
8+
gql,
9+
} from "@apollo/client/core";
10+
import { split } from "@apollo/client/link/core";
11+
import { HttpLink } from "@apollo/client/link/http";
12+
import { isLiveQueryOperationDefinitionNode } from "@n1ru4l/graphql-live-query";
13+
import { print, getOperationAST } from "graphql";
14+
15+
type SSELinkOptions = EventSourceInit & { uri: string };
16+
17+
class SSELink extends ApolloLink {
18+
constructor(private options: SSELinkOptions) {
19+
super();
20+
}
21+
22+
public request(operation: Operation): Observable<FetchResult> {
23+
const url = new URL(this.options.uri);
24+
url.searchParams.append("query", print(operation.query));
25+
url.searchParams.append(
26+
"extensions",
27+
JSON.stringify(operation.operationName)
28+
);
29+
url.searchParams.append("variables", JSON.stringify(operation.variables));
30+
if (operation.extensions) {
31+
url.searchParams.append(
32+
"extensions",
33+
JSON.stringify(operation.extensions)
34+
);
35+
}
36+
37+
return new Observable((sink) => {
38+
const eventsource = new EventSource(url.toString(), this.options);
39+
eventsource.onmessage = function (event) {
40+
const data = JSON.parse(event.data);
41+
sink.next(data);
42+
if (eventsource.readyState === 2) {
43+
sink.complete();
44+
}
45+
};
46+
eventsource.onerror = function (error) {
47+
sink.error(error);
48+
};
49+
return () => eventsource.close();
50+
});
51+
}
52+
}
53+
54+
export const createHTTPApolloLink = (uri: string) => {
55+
const sseLink = new SSELink({
56+
uri,
57+
});
58+
59+
const httpLink = new HttpLink({
60+
uri,
61+
});
62+
63+
const link = split(
64+
({ query, operationName, variables }) => {
65+
const definition = getOperationAST(query, operationName);
66+
const isSubscription =
67+
definition?.kind === "OperationDefinition" &&
68+
definition.operation === "subscription";
69+
70+
const isLiveQuery =
71+
!!definition &&
72+
isLiveQueryOperationDefinitionNode(definition, variables);
73+
74+
return isSubscription || isLiveQuery;
75+
},
76+
sseLink,
77+
httpLink
78+
);
79+
80+
return link;
81+
};

packages/todo-example/client-apollo/src/createApolloClient.tsx renamed to packages/todo-example/client-apollo/src/apollo-link/create-socket-io-apollo-link.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { SocketIOGraphQLClient } from "@n1ru4l/socket-io-graphql-client";
1+
import {
2+
createSocketIOGraphQLClient,
3+
SocketIOGraphQLClient,
4+
} from "@n1ru4l/socket-io-graphql-client";
25
import { applyAsyncIterableIteratorToSink } from "@n1ru4l/push-pull-async-iterable-iterator";
36
import { applyLiveQueryJSONPatch } from "@n1ru4l/graphql-live-query-patch-json-patch";
47
import {
@@ -10,6 +13,7 @@ import {
1013
FetchResult,
1114
} from "@apollo/client";
1215
import { print } from "graphql";
16+
import { io } from "socket.io-client";
1317

1418
class SocketIOGraphQLApolloLink extends ApolloLink {
1519
private networkLayer: SocketIOGraphQLClient<FetchResult>;
@@ -34,10 +38,11 @@ class SocketIOGraphQLApolloLink extends ApolloLink {
3438
}
3539
}
3640

37-
export const createApolloClient = (
38-
networkInterface: SocketIOGraphQLClient<FetchResult>
39-
) =>
40-
new ApolloClient({
41-
link: new SocketIOGraphQLApolloLink(networkInterface),
42-
cache: new InMemoryCache(),
43-
});
41+
export const createSocketIOApolloLink = () => {
42+
let host =
43+
new URLSearchParams(window.location.search).get("host") ?? undefined;
44+
const socket = host ? io(host) : io();
45+
const networkInterface = createSocketIOGraphQLClient<FetchResult>(socket);
46+
47+
return new SocketIOGraphQLApolloLink(networkInterface);
48+
};
Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,65 @@
11
import React from "react";
22
import ReactDOM from "react-dom";
3-
import { io } from "socket.io-client";
43
import "todomvc-app-css/index.css";
5-
import { ApolloProvider, FetchResult } from "@apollo/client";
6-
import { createSocketIOGraphQLClient } from "@n1ru4l/socket-io-graphql-client";
7-
import type { FetcherResult } from "graphiql";
8-
import type { GraphiQLWidget as GraphiQLWidgetType } from "./GraphiQLWidget";
9-
10-
import { createApolloClient } from "./createApolloClient";
4+
import {
5+
ApolloClient,
6+
ApolloLink,
7+
ApolloProvider,
8+
InMemoryCache,
9+
NormalizedCacheObject,
10+
} from "@apollo/client";
1111
import { TodoApplication } from "./TodoApplication";
1212

13-
let host = new URLSearchParams(window.location.search).get("host") ?? undefined;
14-
const socket = host ? io(host) : io();
15-
const networkInterface = createSocketIOGraphQLClient<FetchResult>(socket);
16-
const apolloClient = createApolloClient(networkInterface);
17-
18-
// we only want GraphiQL in our development environment!
19-
let GraphiQLWidget = (): React.ReactElement | null => null;
20-
if (process.env.NODE_ENV === "development") {
21-
GraphiQLWidget = () => {
22-
const [Component, setComponent] = React.useState<
23-
typeof GraphiQLWidgetType | null
24-
>(null);
13+
const createApolloClient = (link: ApolloLink) =>
14+
new ApolloClient({
15+
link,
16+
cache: new InMemoryCache(),
17+
});
2518

26-
React.useEffect(() => {
27-
import("./GraphiQLWidget").then(({ GraphiQLWidget }) => {
28-
setComponent(() => GraphiQLWidget);
29-
});
30-
}, []);
19+
const Root = (): React.ReactElement | null => {
20+
const [client, setClient] =
21+
React.useState<ApolloClient<NormalizedCacheObject> | null>(null);
3122

32-
return Component ? (
33-
<Component
34-
fetcher={({ query: operation, variables, operationName }) =>
35-
networkInterface.execute({
36-
operation,
37-
variables,
38-
operationName,
39-
}) as AsyncIterableIterator<FetcherResult>
23+
React.useEffect(() => {
24+
const params = new URLSearchParams(window.location.search);
25+
if (params.get("sse")) {
26+
let host = params.get("host") ?? undefined;
27+
import("./apollo-link/create-http-apollo-link").then(
28+
async ({ createHTTPApolloLink }) => {
29+
setClient(
30+
createApolloClient(
31+
createHTTPApolloLink(
32+
(host ??
33+
`${window.location.protocol}//${window.location.host}`) +
34+
"/graphql"
35+
)
36+
)
37+
);
38+
}
39+
);
40+
} else {
41+
import("./apollo-link/create-socket-io-apollo-link").then(
42+
async ({ createSocketIOApolloLink }) => {
43+
setClient(createApolloClient(createSocketIOApolloLink()));
4044
}
41-
/>
42-
) : null;
43-
};
44-
}
45+
);
46+
}
47+
}, []);
4548

46-
ReactDOM.render(
47-
<React.StrictMode>
48-
<ApolloProvider client={apolloClient}>
49+
if (client === null) {
50+
return null;
51+
}
52+
53+
return (
54+
<ApolloProvider client={client}>
4955
<TodoApplication />
50-
<GraphiQLWidget />
5156
</ApolloProvider>
57+
);
58+
};
59+
60+
ReactDOM.render(
61+
<React.StrictMode>
62+
<Root />
5263
</React.StrictMode>,
5364
document.getElementById("root")
5465
);

packages/todo-example/client-apollo/vite.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export default defineConfig({
2121
target: backendAddress,
2222
ws: true,
2323
},
24+
"/graphql": {
25+
target: backendAddress,
26+
},
2427
},
2528
},
2629
});

0 commit comments

Comments
 (0)