GraphQL Subscriptions - subscriptionsClient.subscribe is not a function - node.js

So, I'm trying create a basic GraphQL Subscription Server. Problem in request result in graphiql. It's - "subscriptionsClient.subscribe is not a function". I don't understand where's problem.
For GraphQL Subscription Server I have used: graphql-server-express,
subscriptions-transport-ws, graphql-subscriptions
So, it's the task for you, GraphQL masters.
Code:
index.js
const { createServer } = require('http')
const app = require('express')();
const bodyParser = require('body-parser')
const { graphqlExpress, graphiqlExpress } = require('graphql-server-express')
const { SubscriptionServer } = require('subscriptions-transport-ws')
const { subscribe, execute } = require('graphql');
const schema = require('./schema');
app.use(bodyParser.json());
app.use('/graphql', new graphqlExpress({
schema
}));
app.use('/graphiql', new graphiqlExpress({
endpointURL: '/graphql',
subscriptionsEndpoint: 'ws://localhost:4000/subscriptions'
}));
const server = createServer(app);
server.listen(4000, () => {
console.log("Server is listening on port 4000!");
subscriptionServer = SubscriptionServer.create(
{
schema,
execute,
subscribe,
onConnect: () => console.log("Client connected!")
}, {
server,
path: '/subscriptions'
}
);
});
schema.js
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLNonNull,
GraphQLList,
GraphQLID,
GraphQLString
} = require('graphql');
const { PubSub, withFilter } = require('graphql-subscriptions');
const socket = new PubSub();
const store = [];
const NameType = new GraphQLObjectType({
name: "Name",
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString }
}
});
const RootQuery = new GraphQLObjectType({
name: "RootQuery",
fields: {
names: {
type: new GraphQLList(NameType),
resolve: () => store
}
}
});
const RootMutation = new GraphQLObjectType({
name: "RootMutation",
fields: {
addName: {
type: NameType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) }
},
resolve(_, { name }) {
let model = {
id: store.length,
name
}
socket.publish("names", model);
store.push(model);
return model;
}
}
}
});
const RootSubscription = new GraphQLObjectType({
name: "RootSubscription",
fields: {
names: {
type: NameType,
resolve() {
console.log("IS RUNNING");
},
subscribe: withFilter(() => pubsub.asyncIterator("names"), (payload, variables) => {
return payload.names.id === variables.relevantId;
})
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: RootMutation,
subscription: RootSubscription
});

Ok, here's thing.
I've created a fully responsive GraphQL Subscriptions Server using Apollo-Server.
Just use apollo-server-express and apollo-server packages for this task. ApolloServer provides GraphQL Playground that is supports subscriptions. So it's easy to debug and use it on front-end.
Good Luck!

Here is the latest one. This is based on ApolloGraphql Documentation. This worked perfectly for me.
app.js
import mongoose from 'mongoose'
import cors from 'cors';
import dotEnv from 'dotenv'
import http from 'http';
import { ApolloServer, PubSub } from 'apollo-server-express';
import schema from './graphql/schema'
import express from 'express';
dotEnv.config();
const port = process.env.PORT || 3000;
const pubsub = new PubSub();
const app = express();
const server = new ApolloServer({
schema,
subscriptions: {
onConnect: () => console.log('πŸ•ΈοΈ Client connected to websocket'),
onDisconnect: (webSocket, context) => {
console.log('Client disconnected from websocket')
},
},
});
server.applyMiddleware({ app })
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen(port, () => {
console.log(`πŸš€ Apollo Server Server ready at http://localhost:${port}${server.graphqlPath}`)
})
package.json
"dependencies": {
"apollo-server": "^2.25.1",
"apollo-server-express": "^2.25.1",
"babel-node": "^0.0.1-security",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"cross-fetch": "^3.1.4",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"graphql": "^15.5.0",
"graphql-subscriptions": "^1.2.1",
"graphql-tools": "^7.0.5",
"moment": "^2.29.1",
"mongoose": "^5.12.13",
"subscriptions-transport-ws": "^0.9.19"
},
"devDependencies": {
"#babel/cli": "^7.14.3",
"#babel/core": "^7.14.3",
"#babel/node": "^7.14.2",
"#babel/preset-env": "^7.14.4",
"#babel/register": "^7.13.16",
"jest": "^27.0.4",
"nodemon": "^2.0.7",
"supertest": "^6.1.3"
}

import { w3cwebsocket } from 'websocket';
...
const myWSClient = createClient({
url: websockerUrl || "",
webSocketImpl: w3cwebsocket,
});
const wsLink = new GraphQLWsLink(myWSClient);

Related

Apollo GraphQL cannot query _service

I'm new in GraphQL. When I copied this code from Apollo GraphQL docs (and just add one more line for console.log error), I have this issue. Please let me know how to fix it.
const { ApolloServer, gql } = require('apollo-server-express');
const express = require('express');
const typeDefs = gql`
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
const books = [
{
title: 'The Awakening',
author: 'Kate Chopin',
},
{
title: 'City of Glass',
author: 'Paul Auster',
},
];
const resolvers = {
Query: {
books: () => books,
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
formatError: (err) => console.log(err), // <-- only add 1 more line
});
(async () => {
const app = express();
await server.start();
server.applyMiddleware({ app });
app.listen(4000);
})();
And here is my package.json
"dependencies": {
"apollo-server-express": "^3.6.3",
"express": "^4.17.2",
"graphql": "^16.3.0"
}
Then, when I open graphql studio playground at http://localhost:4000/graphql -> click on Query your server -> I got this error on terminal
[ValidationError: Cannot query field "_service" on type "Query".] {
locations: [ { line: 2, column: 9 } ],
path: undefined,
extensions: {
code: 'GRAPHQL_VALIDATION_FAILED',
exception: { stacktrace: [Array] }
}
}```
I have checked again and this is an error from inside GrapqhQL Studio and it was fixed now.

Missing multipart field β€˜operations’ with graphql-upload and apollo-server

Hello I am trying to upload a file using graphql in order to do this I have the following code:
index.js
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const { graphqlUploadExpress } = require('graphql-upload');
async function startApolloServer() {
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
Mutation: {
singleUpload: async (parent, { file }) => {
const { createReadStream, filename, mimetype, encoding } = await file;
const stream = createReadStream();
const out = require('fs').createWriteStream(filename);
stream.pipe(out);
return { filename, mimetype, encoding, url: '' }
}
}
};
const server = new ApolloServer({
typeDefs:gql`
type Query {
hello: String!
}
type UploadedFileResponse {
filename: String!
mimetype: String!
encoding: String!
url: String!
}
type Mutation {
singleUpload(file: Upload!): UploadedFileResponse!
}`
, resolvers },{uploads:false});
await server.start();
const app = express();
app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));
server.applyMiddleware({ app });
await new Promise(resolve => app.listen({ port: 4000 }, resolve));
console.log(`πŸš€ Server ready at http://localhost:4000${server.graphqlPath}`);
return { server, app };
}
startApolloServer()
And in order to call the endpoint I am using the following curl:
curl localhost:4000/graphql \
-F operations='{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { filename } }", "variables": { "file": null } }' \
-F map='{ "0": ["variables.file"] }' \
-F 0=#/home/user/a.txt
But when I execute this I got the following error:
Missing multipart field β€˜operations’
If I remove app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));
I got another error
function deprecated(...args) {
RangeError: Maximum call stack size exceeded
But in this case I can write a empty file with the correct name.
Any idea about how can I finish this poc in order to upload a file using apollo-graphql.
Package.json
"dependencies": {
"apollo-server": "^2.25.2",
"apollo-server-express": "^2.25.2",
"graphql": "^15.5.1",
"graphql-upload": "^12.0.0"
},
"devDependencies": {
"nodemon": "^2.0.12"
},
But I tried whit this too
"resolutions": {
"**/**/fs-capacitor": "^6.2.0",
"**/graphql-upload": "^11.0.0"
}
Node version: 14.16.1 and 14.17.3
Thanks
In your typDefs, add: scalar Upload
In resolvers, add: Upload: GraphQLUpload
GraphQLUpload is imported from 'graphql-upload': import { GraphQLUpload } from "graphql-upload";

Apollo Uploading Files, createReadStream is not a function

I tried to get this working for several days and getting now where. I do believe that that the client side is function properly but I'm not hundred percent certain. I know the resolver on the server is getting called. At one point I was able to destructure the file param in the resolver. But now when I destructure, I get undefined. Not sure what is up with that at one point it was destructuring just fine. The client project is setup to connect to two different GraphQL servers, one is for standard database and subscriptions and the other is file uploader. That is why I left the subscription in the dependences of the client.
I wanna say thank you for taking the time to look at this.
My Server Version Info
"#graphql-tools/schema": "^7.1.5",
"apollo-cache-inmemory": "^1.6.6",
"apollo-server-express": "^2.25.0",
"express": "^4.17.1",
"express-jwt": "^6.0.0",
"graphql": "^15.5.0",
"graphql-middleware": "^6.0.10",
"graphql-shield": "^7.5.0",
"graphql-upload": "^12.0.0",
My Main File
import { ApolloError, ApolloServer, makeExecutableSchema } from "apollo-server-express"
import { applyMiddleware } from "graphql-middleware"
import express from "express"
import { graphqlUploadExpress } from 'graphql-upload'
import makeDir from 'make-dir'
const app = express()
const cors = require('cors')
app.use(cors())
const bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 }));
const apolloServer = new ApolloServer({
uploads: false,
...
schema: applyMiddleware(
makeExecutableSchema({ typeDefs, resolvers }),
permissions
),
context: ({ req, res }) => {
...
},
});
apolloServer.applyMiddleware({ app });
...
async function StartServer() {
await makeDir(UPLOAD_DIR)
app.listen({ host: HOST, port: PORT }, () => {
console.log(`
πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€πŸš€
Apollo File Server ready at http://${HOST}:${PORT}${apolloServer.graphqlPath}
Started at ${datetime}
isDevelopmentMode ${isDevelopmentMode}
`)
});
}
StartServer()
My Schema
type File {
name: String!
size: Int!
}
extend type Mutation {
singleUpload(file: Upload!): File!,
}
My Resolver
singleUpload: async (parent, { file }) => {
console.log('singleUpload------------------------------------------------------------------------------>>>', file)
console.log('singleUpload------------------------------------------------------------------------------>>> 0')
const { createReadStream, filename, mimetype, encoding } = await file;
console.log('singleUpload------------------------------------------------------------------------------>>> 1', filename)
const stream = createReadStream();
console.log('singleUpload------------------------------------------------------------------------------>>> 2')
const pathName = `${UPLOAD_DIR}${filename}`;
console.log('singleUpload------------------------------------------------------------------------------>>> 3')
await stream.pipe(fs.createWriteStream(pathName));
console.log('singleUpload------------------------------------------------------------------------------>>> 4')
return {
name: `http://localhost:4001/images/${filename}`,
size: 1,
};
},
My Client side
Version Info
"#apollo/client": "^3.2.7",
"#apollo/react-hoc": "^4.0.0",
"#apollo/react-hooks": "^4.0.0",
"#graphql-codegen/introspection": "^1.18.1",
"#graphql-codegen/schema-ast": "^1.18.1",
"#graphql-tools/load-files": "^6.2.5",
"#graphql-tools/merge": "^6.2.6",
"apollo-boost": "^0.4.9",
"apollo-link-ws": "^1.0.20",
"apollo-upload-client": "^16.0.0",
"graphql": "^14.7.0",
"graphql-scalars": "^1.7.0",
"react": "^16.14.0",
"react-apollo": "^3.1.5",
"react-app-rewire-define-plugin": "^1.0.0",
"react-app-rewired": "^2.1.8",
"react-dom": "^16.14.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^3.4.1",
"subscriptions-transport-ws": "^0.9.18",
"typescript": "^3.7.3",
The Apollo Client
const uploadLink = createUploadLink({
uri: 'http://localhost:4001/graphql', // Apollo Server is served from port 4000
headers: {
"keep-alive": "true",
'content-type': 'application/json',
...((accessToken !== undefined && accessToken !== null) ? { Authorization: `Bearer ${accessToken}` } : {}),
...((accessToken !== undefined && accessToken !== null) ? { 'x-token': accessToken } : {}),
...((refreshToken !== undefined && refreshToken !== null) ? { 'x-refresh-token': refreshToken } : {}),
}
})
const clientUpload = new ApolloClient({
cache: cache,
link: uploadLink
})
The Uploader Component
const mutUploadFile = gql`
mutation singleUpload($file: Upload!) {
singleUpload(file: $file) {
name
#filename
#mimetype
#encoding
#url
}
}`
export function FileUploadSimpleA() {
const onChange = ((e: any) => {
console.log( 'onChange-------------------------')
const file = e.target.files[0]
console.log('file:', file)
if (file)
clientUpload.mutate({
mutation: mutUploadFile,
variables: {
file: file
}
})
.then(result => {
console.log( 'then-------------------------')
console.log(result)
clientUpload.resetStore();
})
.catch(error => {
console.log( 'catch-------------------------')
console.log(error)
}
);
});
return <input type="file" required onChange={onChange} />;
}
export default FileUploadSimpleA
Console of the browser
fileUploadSimple.tsx:50 onChange-------------------------
fileUploadSimple.tsx:52 file:: FileΒ {name:
"322abcbafe59a52481e6a0b6b84ffbc4.jpg", lastModified: 1614644398495, lastModifiedDate: Mon Mar 01 2021 18:19:58 GMT-0600 (Central Standard Time), webkitRelativePath: "", size: 68817, …}
fileUploadSimple.tsx:66 catch-------------------------
fileUploadSimple.tsx:67 Error: createReadStream is not a function

Unknown type "Upload" - Apollo Server Express with graphql-upload Node 14

I am working with GraphQL and is facing an issue with File Upload. My Node version is v14.16.0
I am getting error as below
Error: Unknown type "Upload". Did you mean "Float"?
Below are the package version installed
Package versions
"dependencies": {
"apollo-server-express": "^2.23.0",
"bcryptjs": "^2.4.3",
"compression": "^1.7.4",
"consola": "^2.15.3",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"graphql-upload": "^11.0.0",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.12.5",
"nodemon": "^2.0.7",
"yup": "^0.32.9"
}
Below is my server setup done with apollo-server-express and graphql-upload package
import express from "express";
import consola from "consola";
import { ApolloServer } from "apollo-server-express";
const { error, success } = consola;
import { PORT } from "./config/index.js";
import { typeDefs, resolvers } from "./graphql/index.js";
import * as AppModels from "./models/index.js";
import compression from "compression";
import { graphqlUploadExpress } from "graphql-upload";
//Initialize the app
const app = express();
app.use(compression());
app.use(
graphqlUploadExpress({
maxFileSize: 30000000,
maxFiles: 20,
})
);
const server = new ApolloServer({
typeDefs,
resolvers,
uploads: false,
context: {
...AppModels,
},
});
app.use(express.static("public"));
server.applyMiddleware({ app });
app.listen(PORT, () => {
success({
badge: true,
message: `Server started at ${PORT}`,
});
});
Here are my type definition for imageUpload Mutation
import { gql } from "apollo-server-express";
export default gql`
extend type Mutation {
imageUploader(file: Upload!): String!
}
`;
My resolver looks like this
import { createWriteStream } from "fs";
import { parse, join } from "path";
import { URL } from "../../config/index.js";
export default {
Mutation: {
imageUploader: async (_, { file }) => {
let { filename, createReadStream } = await file;
let stream = createReadStream();
let { name, ext } = parse(filename);
name = name.replace(/([^a-z0-9 ]+)/gi, "-").replace(" ", "_");
let serverFile = `D:/${name}${ext}`;
let writeStream = await createWriteStream(serverFile);
await stream.pipe(writeStream);
//serverFile = `${URL}/${serverFile.split("uploads")[1]}`;
return serverFile;
},
},
};
Well this is for anybody who faces the situation
As we are using the graphl-upload package so we need to define the custom scalar in typedef and assign the Upload type in resolver.
So now my typedef looks like this. Notice the code scalar Upload
import { gql } from "apollo-server-express";
export default gql`
scalar Upload
extend type Mutation {
imageUploader(file: Upload!): String!
}
`;
After typedef changes we need to define a resolver for that scalar. So now the resolver looks like this
import { createWriteStream } from "fs";
import { GraphQLUpload } from "graphql-upload";
import { parse, join } from "path";
import { URL } from "../../config/index.js";
export default {
Upload: GraphQLUpload,
Mutation: {
imageUploader: async (_, { file }) => {
let { filename, createReadStream } = await file;
let stream = createReadStream();
let { name, ext } = parse(filename);
name = name.replace(/([^a-z0-9 ]+)/gi, "-").replace(" ", "_");
let serverFile = `D:/${name}${ext}`;
let writeStream = await createWriteStream(serverFile);
await stream.pipe(writeStream);
//serverFile = `${URL}/${serverFile.split("uploads")[1]}`;
return serverFile;
},
},
};
Above code helped me in getting pass that issue.
I'm using NODE 14
import {ApolloServer} from "apollo-server-express";
...
// If you want to see uploaded images, then you need to add these commands and,
// create a folder to upload images name like 'uploads'
const app = express();
app.use(express.static(join(__dirname, './uploads')));
app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));
const httpServer = http.createServer(app);
const server = new ApolloServer({
uploads: false, // --------- use this
typeDefs,
resolvers,
plugins: [IN_PROD ? ApolloServerPluginLandingPageGraphQLPlayground() :
ApolloServerPluginLandingPageDisabled(),
ApolloServerPluginDrainHttpServer({httpServer})],
context: {
...AppModels
}
});
after that, in typeDefs,
import { gql } from "apollo-server-express";
export default gql`
scalar Upload // ---- use this
extend type Mutation {
imageUploader(file: Upload!): String!
}
`;
after that, In resolvers
import {parse, join} from 'path';
import {createWriteStream} from 'fs';
import {URL} from "../../../config";
import {GraphQLUpload} from "graphql-upload";
export default {
Upload: GraphQLUpload, // ----- use this
Query: {
info: () => "Hello i am image resolver methods."
},
Mutation: {
imageUploader: async (_, {file}) => {
try {
let {
filename,
createReadStream
} = await file;
let stream = createReadStream();
let {
ext,
name
} = parse(filename);
name = name.replace(/([^a-z0-9 ]+)/gi, '-').replace(' ', '_');
let serverFile = join(__dirname,
`../../../uploads/${name}-${Date.now()}${ext}`);
let writeStream = await createWriteStream(serverFile);
await stream.pipe(writeStream);
let lastPath = serverFile.split('uploads')[1];
lastPath = lastPath.replace('\\', '/');
serverFile = `${URL}${lastPath}`;
return serverFile;
} catch (e) {
console.log("ERROR UPLOAD A IMAGE", e);
throw e;
}
}
}
}
Github link

server.register() function throwing "register is missing", even though i have properly registered the plugins for graphql

I have created a node js project with graph ql (with a very basic schema) but when i am trying to start the server after registering the plugins for graphql and graphiql, i am getting the register is missing error. Below is my code
const hapi=require('hapi');
const { graphqlHapi, graphiqlHapi } = require('apollo-server-hapi');
const { makeExecutableSchema } = require('graphql-tools');
const graphqlSchema = require('./graphql/schema');
const createResolvers = require('./graphql/resolvers');
const executableSchema = makeExecutableSchema({
typeDefs: [graphqlSchema],
resolvers: createResolvers(),
});
const server=hapi.server({
port: 4000,
host:'localhost'
});
server.register({
plugin: graphqlHapi,
options: {
path: '/graphql',
graphqlOptions: () => ({
pretty: true,
schema: executableSchema,
}),
},
});
server.register({
plugin: graphiqlHapi,
options: {
path: '/graphiql',
graphiqlOptions: {
endpointURL: '/graphql',
},
},
});
const init= async()=>{
routes(server);
await server.start();
console.log(`Server is running at: ${server.info.uri}`);
}
init();
I had initially given the key name as register instead of plugin in the server.register() functions. In either case, i am getting the below error
(node:19104) DeprecationWarning: current URL string parser is
deprecated, and will be removed in a future version. To use the new
parser, pass option { useNewUrlParser: true } to MongoClient.connect.
(node:19104) UnhandledPromiseRejectionWarning: AssertionError
[ERR_ASSERTION]: I nvalid plugin options {
"plugin": {
"options": {
"path": "/graphql",
"graphqlOptions": () => ({\r\n pretty: true,\r\n schema: exe cutableSchema,\r\n })
},
"register" [1]: -- missing -- } }
Please help me out in understanding whenter code herey this happening and how it can be rectified.
Below is the dependencies in my project
apollo-server-hapi": "^2.3.1", "graphql": "^14.0.2", "graphql-tools":
"^4.0.3", "hapi": "^17.8.1",
EDIT
Code after making the suggested changes
const hapi=require('hapi');
const { graphqlHapi, graphiqlHapi } = require('apollo-server-hapi');
const { makeExecutableSchema } = require('graphql-tools');
const graphqlSchema = require('./graphql/schema');
const createResolvers = require('./graphql/resolvers');
const executableSchema = makeExecutableSchema({
typeDefs: [graphqlSchema],
resolvers: createResolvers(),
});
async function start_server() {
const server=hapi.server({
port: 4000,
host:'localhost'
});
await server.register({
plugin: graphqlHapi,
options: {
path: '/graphql',
graphqlOptions: () => ({
pretty: true,
schema: executableSchema,
}),
route: {
cors: true,
},
},
});
await server.register({
plugin: graphiqlHapi,
options: {
path: '/graphiql',
graphiqlOptions: {
endpointURL: '/graphql',
},
route: {
cors: true,
},
},
});
try {
await server.start();
console.log(`Server is running at: ${server.info.uri}`);
} catch (err) {
console.log(`Error while starting server: ${err.message}`)
}
}
start_server();
There is no need to register the plugins in the latest release of apollo-server-hapi. It contains GraphQL playground instead of graphiql.
The below changes need to be done instead of registering.
const {ApolloServer} = require('apollo-server-hapi');
const executableSchema = makeExecutableSchema({
typeDefs: [graphqlSchema],
resolvers: createResolvers(),
});
const server = new ApolloServer({
schema:executableSchema
});
async function start_server() {
const app=hapi.server({
port: 4000,
host:'localhost'
});
await server.applyMiddleware({ app });
try {
await app.start();
console.log(`Server is running at: ${app.info.uri}`);
} catch (err) {
console.log(`Error while starting server: ${err.message}`)
}
}
start_server();

Resources