In graphql yoga documentation, I found this example for using graphql yoga with websockets but it's in nodejs environment. How can I setup a server in nextjs api using this example? All advice is appreciated, thanks.
import { createServer } from '#graphql-yoga/node'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
async function main() {
const yogaApp = createServer({
graphiql: {
// Use WebSockets in GraphiQL
subscriptionsProtocol: 'WS'
}
})
// Get NodeJS Server from Yoga
const httpServer = await yogaApp.start()
// Create WebSocket server instance from our Node server
const wsServer = new WebSocketServer({
server: httpServer,
path: yogaApp.getAddressInfo().endpoint
})
// Integrate Yoga's Envelop instance and NodeJS server with graphql-ws
useServer(
{
execute: (args: any) => args.rootValue.execute(args),
subscribe: (args: any) => args.rootValue.subscribe(args),
onSubscribe: async (ctx, msg) => {
const { schema, execute, subscribe, contextFactory, parse, validate } =
yogaApp.getEnveloped(ctx)
const args = {
schema,
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
contextValue: await contextFactory(),
rootValue: {
execute,
subscribe
}
}
const errors = validate(args.schema, args.document)
if (errors.length) return errors
return args
}
},
wsServer
)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
So I have a GraphQL server that I wrote in Go, following this tutorial pretty closely. I have my front-end written as a Next.js application, and I am currently trying to create a client to connect to my server and even following the subscription docs to the T, I cannot seem to get it to work. How is it that the examples provided do not include a webSocketImpl?
If I don't provide a webSocketImpl, I get this:
Error: WebSocket implementation missing; on Node you can `import WebSocket from 'ws';` and pass `webSocketImpl: WebSocket` to `createClient`
So, naturally, I import { WebSocket } from "ws"; , and have:
const wsLink = new GraphQLWsLink(
createClient({
webSocketImpl: WebSocket,
url: "ws://localhost:8080/subscriptions",
})
);
Where I then get:
error - ./node_modules/node-gyp-build/index.js:1:0
Module not found: Can't resolve 'fs'
Here is the full code, basically all I need is to create a ApolloClient and export it for use in my React code.
import { ApolloClient, HttpLink, InMemoryCache, split } from "#apollo/client";
import { GraphQLWsLink } from "#apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
import { getMainDefinition } from "#apollo/client/utilities";
import { WebSocket } from "ws";
const wsLink = new GraphQLWsLink(
createClient({
webSocketImpl: WebSocket,
url: "ws://localhost:8080/subscriptions",
})
);
const httpLink = new HttpLink({
uri: `http://localhost:8080/query`,
});
const link = split(
({ query }) => {
const def = getMainDefinition(query);
return (
def.kind === "OperationDefinition" && def.operation === "subscription"
);
},
wsLink,
httpLink
);
export const Client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
Am I totally missing something here? Is there not a default WebSocket implementation in my installation? Obviously the "ws" implementation isn't cutting it, probably because fs is not available in-browser?
A major thing I left off here: I was using Next.js. The reason this was occurring was due to SSR. Basically, we need to only generate the WebSocket link if we are in the browser by using `typeof window !== 'undefined'. This is my updated code:
import { ApolloClient, HttpLink, InMemoryCache, split } from "#apollo/client";
import { GraphQLWsLink } from "#apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
import { getMainDefinition } from "#apollo/client/utilities";
const wsLink =
typeof window !== "undefined"
? new GraphQLWsLink(
createClient({
url: "ws://localhost:8080/subscriptions",
})
)
: null;
const httpLink = new HttpLink({
uri: `http://localhost:8080/query`,
});
const link =
typeof window !== "undefined" && wsLink != null
? split(
({ query }) => {
const def = getMainDefinition(query);
return (
def.kind === "OperationDefinition" &&
def.operation === "subscription"
);
},
wsLink,
httpLink
)
: httpLink;
export const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
I found a way how it`s work with GraphQL-yoga
It's client :
// import { createServer } from "#graphql-yoga/node";
import { makeExecutableSchema } from "#graphql-tools/schema";
import typeDefs from "#/server/graphql/typeDef/schema.graphql";
import resolvers from "#/server/graphql/resolvers";
import dbInit from "#/lib/dbInit";
import JWT from "jsonwebtoken";
import Cors from "micro-cors";
// const pubsub = new PubSub();
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
import {
createServer,
createPubSub,
GraphQLYogaError,
} from "#graphql-yoga/node";
import { useResponseCache } from "#envelop/response-cache";
import { WebSocketServer } from "ws"; // yarn add ws
// import ws from 'ws'; yarn add ws#7
// const WebSocketServer = ws.Server;
import { useServer } from "graphql-ws/lib/use/ws";
const pubSub = createPubSub();
const server = createServer({
cors: {
credentials: "same-origin",
origin: ["http://localhost:3000"], // your frontend url.
},
plugins: [
useResponseCache({
includeExtensionMetadata: true,
}),
],
context: async (ctx) => {
let wsServer = null;
wsServer = ctx.res.socket.server.ws ||= new WebSocketServer({
port: 4000,
path: "/api/graphql",
});
wsServer &&= useServer({ schema }, wsServer);
const db = await dbInit();
let { token, customerId, customerExpire } = ctx.req.cookies;
// 1. Find optional visitor id
let id = null;
if (token) {
try {
let obj = JWT.verify(token, "MY_SECRET");
id = obj.id;
} catch (err) {
console.error("error on apollo server", err); // expired token, invalid token
// TODO try apollo-link-error on the client
throw new AuthenticationError(
"Authentication token is invalid, please log in"
);
}
}
return {
...ctx,
userId: id,
customerId,
pubSub,
};
},
schema,
});
export default server;
And client
import { useMemo } from "react";
import {
ApolloClient,
InMemoryCache,
split,
HttpLink,
createHttpLink,
} from "#apollo/client";
import merge from "deepmerge";
import { getMainDefinition } from "apollo-utilities";
import { GraphQLWsLink } from "#apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
// const link = process.env.SERVER_LINK;
let apolloClient;
//create websocket link
const wsLink =
typeof window !== "undefined"
? new GraphQLWsLink(
createClient({
url: "ws://localhost:4000/api/graphql",
on: {
connected: () => console.log("connected client"),
closed: () => console.log("closed"),
},
})
)
: null;
//create http link
const httplink = new HttpLink({
uri: "http://localhost:3000/api/graphql",
credentials: "same-origin",
});
//Split the link based on graphql operation
const link =
typeof window !== "undefined"
? split(
//only create the split in the browser
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscription";
},
wsLink,
httplink
)
: httplink;
//create apollo client
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === "undefined",
link: link,
cache: new InMemoryCache(),
});
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient();
if (initialState) {
// Get existing cache, loaded during client side data fetching
const existingCache = _apolloClient.extract();
// Merge the existing cache into data passed from getStaticProps/getServerSideProps
const data = merge(initialState, existingCache);
// Restore the cache with the merged data
_apolloClient.cache.restore(data);
}
if (typeof window === "undefined") return _apolloClient;
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}
export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState]);
return store;
}
guys, I have a problem when I want to use a Subscription I'm facing this issue I don't find a solution in any place, I'm a GQL user and i decide to use Subscription to make real-time website but I'm facing a a this issue ,
hare is code
I'm trying to show apollo docs but I'm facing another issue(graphql doesn't find my resolver), so I try to use this pace of code in my mind it's work but the issue is it says
Unable to connect wss://localhost:4001 also I'm trying to use Unable to connect wss://localhost:4001/graphql and Unable to connect wss://localhost:4001/subscription, also i try this three way with using ws
// my resolver
const somethingChanged = () => {
// subscribe: () => {
console.log("subscribe")
pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC)
// }
}
const makeId = () => {
// make id generator 36 symbols
let id = Math.random().toString(36).split(".")[1]
pubsub.publish(SOMETHING_CHANGED_TOPIC, {
somethingChanged: {
id,
},
})
return id
}
const resolvers = {
Subscription: {
somethingChanged,
},
Query: {
hello: () => "Hello world!",
},
Mutation: {
makeId,
},
}
// app.ts
import { createServer } from "http"
import express from "express"
import { ApolloServer, gql } from "apollo-server-express"
import { typeDefs } from "./graphql/schema"
import "colors"
import resolvers from "./graphql/root"
import connect from "./db/connect"
import { PubSub } from "graphql-subscriptions"
const SOMETHING_CHANGED_TOPIC = "something_changed"
require("dotenv").config()
export const pubsub = new PubSub()
// 1 creating one function for app
const startServer = async () => {
// 2 // declaring app as express
const app = express()
const httpServer = createServer(app)
setInterval(() => {
console.log(`Server was work ${Math.random().toString()}`.green)
}, 2000)
// middleware's
connect()
// 5
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => req,
})
// 6
await apolloServer.start()
// 7
apolloServer.applyMiddleware({
app,
path: "/graphql",
})
// 8
httpServer.listen({ port: process.env.PORT || 4001 }, () =>
console.log(
`Server listening on localhost:4001${apolloServer.graphqlPath}`.blue
)
)
}
startServer()
I just want to test in apollo explorer but it doesn't work
To do graphql queries and mutations ive had success with both fetch and svelte-apollo (see https://github.com/timhall/svelte-apollo)
I like the fech approach for its simplicity.
Svelte-apollo features subscriptions and I will try to get it to work.
But are there alternatives?
How do you consume graphql subscriptions with svelte?
Heres my solution, using Apollo:
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql'
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:3000/subscriptions`,
options: {
reconnect: true
}
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
After all that, you have your client set up. Next,
import gql from 'graphql-tag';
client.subscribe({
query: gql`subscription { whatever }`
}).subscribe(result => console.log(result.data);
I'm using urql's svelte bindings. The documentation also shows how to use the bindings with subscriptions.
I'm using Koa.js with Apollo Server's apollo-server-koa.
I've debug the { req } and the value is undefined.
I've followed the documentation, but still got no clue.
Even if I access the req.headers.authorization and put this on HTTP Header of graphql gui:
{
"authorization": "bla"
}
the value is still undefined.
app.ts:
import cors from "#koa/cors";
import Koa from "koa";
import config from "./config/environtment";
import server from "./server";
const PORT: number = config.port;
async function bootstrap() {
try {
const app: Koa = new Koa();
server.applyMiddleware({ app });
app
.use(cors())
.listen(PORT, () =>
console.log(
`Server running on http://localhost:${PORT}${server.graphqlPath}`,
),
);
} catch (error) {
console.error(error);
}
}
bootstrap();
server.ts:
import { ApolloServer } from "apollo-server-koa";
import typeDefs from "./graphql/schema";
import resolvers from "./graphql/resolvers";
import context from "./graphql/context";
export default new ApolloServer({
typeDefs,
resolvers,
context,
});
context.ts
export default ({ req }) => {
console.log(req) // return undefined.
return {
test: "test",
};
};
The docs are specific to apollo-server, which uses apollo-server-express under the hood. I believe for apollo-server-koa, the options are passed in an object with a ctx field that holds the Koa Context. So the following should work:
export default ({ ctx }) => {
console.log(ctx.request)
console.log(ctx.response)
return {};
};