GraphQL WebSocket Router allows you to expose an internal GraphQL API over Websocket without exposing the entire schema.
GraphQL Rest Router is available via NPM as graphql-websocket-router
npm install --save graphql-websocket-router
# GraphQL Subscription
subscription OnEvent($topic: String) {
onEvent(topic: $topic) {
timestamp
message
}
}
# GraphQL Live Query
query GetEpisodeById($id: ID!) {
episode(id: $id) @live {
id
name
air_date
}
}
import GraphQLWebsocketRouter from "graphql-websocket-router";
import { App as uWebSocketApp } from "uWebSockets.js";
import { readFileSync } from "fs";
import { resolve } from "path";
const api = new GraphQLWebsocketRouter(
"ws://localhost:4000/graphql",
readFileSync(resolve(__dirname, "queries.gql"), "utf-8")
);
uWebSocketApp()
.ws("/*", api.uWebSocketBehavior())
.listen(9001, (listenSocket) => {
if (listenSocket) {
console.log("Listening on port 9001");
}
});
// Example: Query
>> { method: "query", operation: "GetEpisodeById", variables: { id: 1 } }
<< { status: "ok", data: { id: 1, name: "Episode One" } }
// Example: Subscription
>> { method: "subscribe", operation: "OnEvent", variables: { topic: 'all' } }
<< { status: "ok", data: { timestamp: 1, message: "hello" } }
<< { status: "update", data: { timestamp: 2, message: "world" } }
<< { status: "update", data: { timestamp: 3, message: "!" } }
// Example: Live Subscribe
>> { method: "subscribe", operation: "GetEpisodeById", variables: { id: 1 } }
<< { status: "ok", data: { id: 1, name: "Episode O" } }
<< { status: "update", data: { name: "Episode On" } }
<< { status: "update", data: { name: "Episode One" } }
// Example: Unsubscribe
>> { method: "unsubscribe", operation: "GetEpisodeById", variables: { id: 1 } }
<< { status: "ok" }
// Example: Error
>> { method: "query", operation: "GetEpisodeById", /* missing id */ }
<< { status: "error", error: { message: "id is missing" } }
Supports live queries, subscriptions, queries and mutations.
// ...
const api = new GraphQLWebsocketRouter(
"ws://localhost:4000/graphql",
readFileSync(resolve(__dirname, "queries.gql"), "utf-8"),
{ enableDeltaPatching: false }
);
// ...
Supports queries and mutations only.
// ...
const api = new GraphQLWebsocketRouter(
"https://rickandmortyapi.com/graphql",
readFileSync(resolve(__dirname, "queries.gql"), "utf-8")
);
// ...
const socket = new WebSocket("ws://localhost:9001");
socket.onopen = (event) => {
// connection established
socket.send(
JSON.stringify({
method: "subscribe",
operation: "GetEpisodeById",
variables: { id: 1 },
})
);
};
socket.onmessage = (event) => {
const { status, data: document } = JSON.parse(event.data);
console.log("[received]", document);
};
socket.onerror = (error) => {
// handle error
};
import { useLiveQuery } from "graphql-websocket-router/client/react";
export default function MyComponent() {
const document = useLiveQuery({
operation: "GetEpisodeById",
variables: { id: 1 },
});
return <pre>{JSON.stringify(document)}</pre>;
}
import {
fetchLiveQuery,
useLiveQuery,
} from "graphql-websocket-router/client/react";
export async function getStaticProps(context) {
const { query: episodeQuery } = await fetchLiveQuery({
operation: "GetEpisodeById",
variables: { id: 1 },
});
return {
props: { episodeQuery },
};
}
export default function EpisodeDetails({ episodeQuery }) {
const episode = useLiveQuery(episodeQuery);
// Render episode...
}