Setting up graphql yoga with websockets in nextjs api - websocket

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)
})

Related

GraphQL subscription isn't triggered after PubSub publish

I'm trying to create a basic subscriptions system using WebSockets, TypeGraphQL and Apollo Server. I've created the WS server and it seems to work correctly.
import express from 'express'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import { ApolloServer } from 'apollo-server-express'
import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core'
import { buildSchema } from 'type-graphql'
import { MessageResolver } from './resolvers/messageResolver'
import { appDataSource } from './dataSource'
import { PubSub } from 'graphql-subscriptions'
import http from 'http'
import { WebSocketServer } from 'ws'
const main = async () => {
appDataSource.initialize().then(() => {
console.log("DataSource initialized.")
})
const app = express()
app.set('trust proxy', 1)
app.use(
cors({
origin: process.env.CORS_ORIGIN,
credentials: true,
})
)
app.use(cookieParser())
const schema = await buildSchema({
resolvers: [MessageResolver],
validate: false
})
const pubsub = new PubSub();
const apolloServer = new ApolloServer({
plugins: [
ApolloServerPluginLandingPageGraphQLPlayground(),
],
schema,
context: ({ req, res }) => ({ req, res, pubsub }),
})
await apolloServer.start()
apolloServer.applyMiddleware({ app, cors: false })
const httpServer = http.createServer()
const wss = new WebSocketServer({
server: httpServer
})
httpServer.on('request', app);
httpServer.listen(5000, function () {
console.log(`http/ws server listening on 5000`);
});
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log(`received: ${message}`);
ws.send(JSON.stringify({
answer: 42
}));
});
});
}
main().catch((err) => {
console.error(err.stack);
})
Here are my subscription and my mutation:
#Subscription({
topics: "NOTIFICATIONS"
})
test(
#Root() message: string,
): string {
return message
}
#Mutation(() => Boolean)
async addNewComment(
#Arg("message") message: string,
#PubSub("NOTIFICATIONS") publish: Publisher<string>
) {
await Message.create({ message }).save()
await publish(message);
return true;
}
The problem I have is that when I use the mutation, the subscription should be triggered. This thing doesn't happen and I don't know why. Do you have any ideas?
Thank you!

apollo explorer say "isTrusted": true when i try use Subscription

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

Next JS server side graphql subscriptions

I am currently building chat app in Next JS. I use graphql with Apollo Client in frontend and Apollo Server on backend. Now I want real time updates, but I found out there is no support for Subscription in apollo-server-micro. As they write here 😢 :
https://www.apollographql.com/docs/apollo-server/data/subscriptions#enabling-subscriptions
Beginning in Apollo Server 3, subscriptions are not supported by the
"batteries-included" apollo-server package. To enable subscriptions,
you must first swap to the apollo-server-express package (or any other
Apollo Server integration package that supports subscriptions).
But I can not use apollo-server-express because I use NextAuth for authentication and then I pass it to context:
export async function createContext({
req,
res,
}: {
req: NextApiRequest;
res: NextApiResponse;
}): Promise<Context> {
const session = await getSession({ req });
const user = { ...session?.user, _id: session?.userId } as User;
const db = await dbConnect();
return {
user,
db,
};
}
Thanks for help 👍.
I found a solution, for everyone who wants to use graphql subscriptions on server: Use graphql-yoga instead :
https://www.graphql-yoga.com/docs/features/subscriptions
My code for pages/api/graphql.ts :
import { createServer, createPubSub, PubSub } from "#graphql-yoga/node";
import { NextApiRequest, NextApiResponse } from "next";
import { Session } from "next-auth";
import { getSession } from "next-auth/react";
const pubSub = createPubSub<{
"user:newMessage": [userId: string, message: Message];
"user:newChat": [userId: string, chat: Chat];
}>();
export type pubSub = typeof pubSub;
const server = createServer<
{
req: NextApiRequest;
res: NextApiResponse;
},
{
user: User;
pubSub: any;
}
>({
context: async ({ req }) => {
const session = await getSession({ req });
await dbConnect();
return {
user: { ...session?.user, _id: session?.userId } as User,
pubSub,
};
},
schema: {
typeDefs,
resolvers: {
Query,
Mutation,
Subscription,
},
},
});
export default server;

Could anyone provide a fastapi websocket endpoint which could connect with the example given for RTK Query streaming updates

I'm trying to get my head around RTK Query as it applies to websockets. The example given is
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
import { createEntityAdapter, EntityState } from '#reduxjs/toolkit'
import { isMessage } from './schemaValidators'
export type Channel = 'redux' | 'general'
export interface Message {
id: number
channel: Channel
userName: string
text: string
}
const messagesAdapter = createEntityAdapter<Message>()
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getMessages: build.query<EntityState<Message>, Channel>({
query: (channel) => `messages/${channel}`,
transformResponse(response: Message[]) {
return messagesAdapter.addMany(
messagesAdapter.getInitialState(),
response
)
},
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
const ws = new WebSocket('ws://localhost:8080')
try {
await cacheDataLoaded
const listener = (event: MessageEvent) => {
const data = JSON.parse(event.data)
if (!isMessage(data) || data.channel !== arg) return
updateCachedData((draft) => {
messagesAdapter.upsertOne(draft, data)
})
}
ws.addEventListener('message', listener)
} catch {}
await cacheEntryRemoved
ws.close()
},
}),
}),
})
export const { useGetMessagesQuery } = api
for the frontend. It looks as though the idea is to make a request to /messages/{channel} and on successful receipt and caching of these messages to connect to a websocket api. I'm struggling to create a fastapi app that connects with this example so I can figure out the workings. Does anyone have an example they might be willing to please share?

Apollo graphql subscriptions, using the same endpoint for the graphql server and websocket endpoint

I was just wondering if there was any performance decrease or any disadvantage in using the same endpoint for the graphql endpoint and also for the WebSocket. You can see the sample code below.
import express = require("express");
import { ApolloServer } from "apollo-server-express";
import bodyParser from "body-parser";
import Knex from "knex";
import { execute, subscribe } from "graphql";
import { SubscriptionServer } from "subscriptions-transport-ws";
// graphql api
import api from "./api";
const { createServer } = require("http");
const app: express.Application = express();
const path = "/graphql";
app.use(bodyParser.json());
const graphqlServer = new ApolloServer(api);
graphqlServer.applyMiddleware({ app, path });
const server = createServer(app);
server.listen(process.env.PORT, err => {
if (err) {
throw new Error(err);
}
new SubscriptionServer(
{
execute,
subscribe,
schema: api.schema
},
{
server,
// same as the graphql endpoint
path
}
);
console.log(
`the server is running at http://localhost:${process.env.PORT}/graphql`
);
});

Resources