Learn about apollo-graphql Subscription - node.js

I'm presently studying Graphql(apollo) and utilising Node.js for the backend.
I've made some progress with queries and mutations, but I'm stuck at subscription. I've seen several videos and read some blogs, but I'm having trouble grasping it since they're utilising front-end frameworks, such as react, and I'm not familiar with react or any other front-end javascript frameworks.
I'm solely interested in learning it for the backend.
Is there anyone who can assist me with this?
I've got three separate queries (or whatever they're called) that I'm working on.
User,
Post and
Comment
Now I want to generate a subscription whenever someone adds a new comment or creates a new post.
For users, comments, and posts, I already have a mutation for add, update and remove.
There is currently no authentication or authorization in place.

Your question is more to help me implement this rather than actually asking an issue or problem you faced after trying out some steps in series of order.
This might help you when learning and implementing GraphQL subscription;
https://www.apollographql.com/docs/apollo-server/data/subscriptions/
https://www.youtube.com/watch?v=R2IrBI4-7Kg&t=698s
If you have tried something and faced a wall, please add your code and what you have done, so I could help you on where you are wrong or what you could do hopefully. Cheers.

The solutions I'm providing are crafted on #apollo/server v.4, with expressMiddleware and mongodb/mongoose on the backend and subscribeToMore with updateQuery on the client-side instead of the useSubscription hook.
The graphql-transport-ws transport library is no longer supported; instead, use graphql-ws.
The implementation differentiates three main collections: User, Post, and Comment, as well as subscription for post creation, post modification, and user authentication.
Likewise, as of 12.2022, the following setup and configuration apply.
Subscription on the backend:
Install the following dependencies:
$ npm i #apollo/server #graphql-tools/schema graphql-subscriptions graphql-ws ws cors body-parser mongoose graphql express
I'm assuming you've already configured your MongoDB models; if not, you might want to look at this repo for a basic setup.
Set up schema types and resolvers, such as this one.
// typeDefs.js
const typeDefs = `#graphql
type User {
id: ID!
email: String!
posts: [Post]!
commentsMade: [Comment]!
}
type Token {
value: String!
}
input UpdatePostInput {
title: String!
}
type Post {
id: ID!
title: String!
postedBy: User
comments: [Comment]!
}
input CommentInput {
text: String!
}
type Comment {
id: ID!
text: String!
commenter: User!
commentFor: Post!
}
type Query {
users: [User]!
user(id: ID!): User!
posts: [Post!]!
post(id: ID!): Post!
comment(id: ID!): Comment!
comments: [Comment!]!
}
type Mutation {
signup(email: String!, password: String!): User
signin(email: String!, password: String!): Token
createPost(title: String): Post
createComment(postId: String!, commentInput: CommentInput!): Comment
updatePost(postId: ID!, updatePostInput: UpdatePostInput!): Post
}
type Subscription {
postAdded: Post
commentAdded: Comment
postUpdated: Post
}
`
export default typeDefs
// resolvers.js
import dotenv from 'dotenv'
import { PubSub } from 'graphql-subscriptions'
import mongoose from 'mongoose'
import { GraphQLError } from 'graphql'
import bcrypt from 'bcrypt'
import UserModel from '../models/User.js'
import PostModel from '../models/Post.js'
import CommentModel from '../models/Comment.js'
dotenv.config()
...
const pubsub = new PubSub()
const User = UserModel
const Post = PostModel
const Comment = CommentModel
const secret = process.env.TOKEN_SECRET
const resolvers = {
Query: {...},
Mutation: {
...
createPost: async (_, args, contextValue) => {
const authUser = contextValue.authUser
if (!authUser) {
throw new GraphQLError('User is not authenticated', {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: 401 },
},
})
}
const post = new Post({
...args,
postedBy: mongoose.Types.ObjectId(authUser.id),
})
try {
const savedPost = await post.save()
authUser.posts = authUser.posts.concat(post._id)
await authUser.save()
const addedPost = {
id: savedPost.id,
title: savedPost.title,
postedBy: savedPost.postedBy,
comments: savedPost.comments,
}
// subscription postAdded with object iterator POST_ADDED
pubsub.publish('POST_ADDED', { postAdded: addedPost })
return post
} catch (error) {
throw new GraphQLError(`Error: ${error.message}`, {
extensions: {
code: 'BAD_USER_INPUT',
http: { status: 400 },
argumentName: args,
},
})
}
},
updatePost: async (_, args, contextValue) => {
const authUser = contextValue.authUser
if (!authUser) {
throw new GraphQLError('User is not authenticated', {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: 401 },
},
})
}
try {
const post = await Post.findByIdAndUpdate(
args.postId,
args.updatePostInput,
{ new: true }
)
.populate('comments')
.populate('postedBy')
const updatedPost = {
id: post.id,
title: post.title,
postedBy: post.postedBy,
comments: post.comments,
}
// subscription postUpdated with object iterator POST_UPDATED
pubsub.publish('POST_UPDATED', { postUpdated: updatedPost })
return post
} catch (error) {
throw new GraphQLError(`Error: ${error.message}`, {
extensions: {
code: 'BAD_REQUEST',
http: { status: 400 },
argumentName: args,
},
})
}
},
},
// resolvers for post addition and post modification using subscribe function
Subscription: {
postAdded: {
subscribe: () => pubsub.asyncIterator('POST_ADDED'),
},
commentAdded: {
subscribe: () => pubsub.asyncIterator('COMMENT_ADDED'),
},
postUpdated: {
subscribe: () => pubsub.asyncIterator('POST_UPDATED'),
},
},
//Hard-coding the default resolvers is appropriate in some cases,
// but I think it is required in fields with references to other
//database models to avoid returning null field values.
Post: {
id: async (parent, args, contextValue, info) => {
return parent.id
},
title: async (parent) => {
return parent.title
},
postedBy: async (parent) => {
const user = await User.findById(parent.postedBy)
.populate('posts', { id: 1, title: 1, comments: 1, postedBy: 1 })
.populate('commentsMade')
//console.log('id', user.id)
//console.log('email', user.email)
return user
},
comments: async (parent) => {
return parent.comments
},
},
Comment: {
id: async (parent, args, contextValue, info) => {
return parent.id
},
text: async (parent, args, contextValue, info) => {
return parent.text
},
commenter: async (parent, args, contextValue, info) => {
const user = await User.findById(parent.commenter)
.populate('posts')
.populate('commentsMade')
return user
},
commentFor: async (parent, args, contextValue, info) => {
const post = await Post.findById(parent.commentFor)
.populate('comments')
.populate('postedBy')
return post
},
},
...
}
export default resolvers
The code in the main entry server file (e.g. index.js) may look like this, e.g.
import dotenv from 'dotenv'
import { ApolloServer } from '#apollo/server'
import { expressMiddleware } from '#apollo/server/express4'
import { ApolloServerPluginDrainHttpServer } from '#apollo/server/plugin/drainHttpServer'
import { makeExecutableSchema } from '#graphql-tools/schema'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
import express from 'express'
import http from 'http'
import cors from 'cors'
import bodyParser from 'body-parser'
import jwt from 'jsonwebtoken'
import UserModel from './models/User.js'
import typeDefs from './tpeDefs.js'
import resolvers from './resolvers.js'
import mongoose from 'mongoose'
dotenv.config()
mongoose.set('strictQuery', false)
let db_uri
if (process.env.NODE_ENV === 'development') {
db_uri = process.env.MONGO_DEV
}
mongoose.connect(db_uri).then(
() => {
console.log('Database connected')
},
(err) => {
console.log(err)
}
)
const startGraphQLServer = async () => {
const app = express()
const httpServer = http.createServer(app)
const schema = makeExecutableSchema({ typeDefs, resolvers })
const wsServer = new WebSocketServer({
server: httpServer,
path: '/',
})
const serverCleanup = useServer({ schema }, wsServer)
const server = new ApolloServer({
schema,
context: async ({ req }) => {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if (token) {
const decoded = jwt.verify(token, process.env.TOKEN_SECRET)
const authUser = await UserModel.findById(decoded.id)
.populate('commentsMade')
.populate('posts')
return { authUser }
}
},
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose()
},
}
},
},
],
})
await server.start()
app.use(
'/',
cors(),
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if (token) {
const decoded = jwt.verify(token, process.env.TOKEN_SECRET)
const authUser = await UserModel.findById(decoded.id)
.populate('commentsMade')
.populate('posts')
return { authUser }
}
},
})
)
const PORT = 4000
httpServer.listen(PORT, () =>
console.log(`Server is now running on http://localhost:${PORT}`)
)
}
startGraphQLServer()
END. It's time to run some tests and checks in the Apollo Explorer sandbox. Be conscientious about defining the required default resolvers to avert the Apollo server from sending null values on your behalf.
To view the code and implementation, go to this repository.
Happy coding!

Related

Unable to get initial data using graphql-ws subscription

I am fairly new to using graphql-ws and graphql-yoga server, so forgive me if this is a naive question or mistake from my side.
I went through graphql-ws documentation. It has written the schema as a parameter. Unfortunately, the schema definition used in the documentation is missing a reference.
After adding a new todo (using addTodo) it shows two todo items. So I believe it is unable to return the initial todo list whenever running subscribe on Yoga Graphiql explorer.
It should show the initial todo item as soon as it has been subscribed and published in the schema definition.
My understanding is there is something I am missing in the schema definition which is not showing the todo list when tried accessing Yoga Graphiql explorer.
Has anyone had a similar experience and been able to resolve it? What I am missing?
Libraries used
Backend
graphql-yoga
ws
graphql-ws
Frontend
solid-js
wonka
Todo item - declared in schema
{
id: "1",
title: "Learn GraphQL + Solidjs",
completed: false
}
Screenshot
Code Snippets
Schema definition
import { createPubSub } from 'graphql-yoga';
import { Todo } from "./types";
let todos = [
{
id: "1",
title: "Learn GraphQL + Solidjs",
completed: false
}
];
// channel
const TODOS_CHANNEL = "TODOS_CHANNEL";
// pubsub
const pubSub = createPubSub();
const publishToChannel = (data: any) => pubSub.publish(TODOS_CHANNEL, data);
// Type def
const typeDefs = [`
type Todo {
id: ID!
title: String!
completed: Boolean!
}
type Query {
getTodos: [Todo]!
}
type Mutation {
addTodo(title: String!): Todo!
}
type Subscription {
todos: [Todo!]
}
`];
// Resolvers
const resolvers = {
Query: {
getTodos: () => todos
},
Mutation: {
addTodo: (_: unknown, { title }: Todo) => {
const newTodo = {
id: "" + (todos.length + 1),
title,
completed: false
};
todos.push(newTodo);
publishToChannel({ todos });
return newTodo;
},
Subscription: {
todos: {
subscribe: () => {
const res = pubSub.subscribe(TODOS_CHANNEL);
publishToChannel({ todos });
return res;
}
},
},
};
export const schema = {
resolvers,
typeDefs
};
Server backend
import { createServer } from "graphql-yoga";
import { WebSocketServer } from "ws";
import { useServer } from "graphql-ws/lib/use/ws";
import { schema } from "./src/schema";
import { execute, ExecutionArgs, subscribe } from "graphql";
async function main() {
const yogaApp = createServer({
schema,
graphiql: {
subscriptionsProtocol: 'WS', // use WebSockets instead of SSE
},
});
const server = await yogaApp.start();
const wsServer = new WebSocketServer({
server,
path: yogaApp.getAddressInfo().endpoint
});
type EnvelopedExecutionArgs = ExecutionArgs & {
rootValue: {
execute: typeof execute;
subscribe: typeof subscribe;
};
};
useServer(
{
execute: (args: any) => (args as EnvelopedExecutionArgs).rootValue.execute(args),
subscribe: (args: any) => (args as EnvelopedExecutionArgs).rootValue.subscribe(args),
onSubscribe: async (ctx, msg) => {
const { schema, execute, subscribe, contextFactory, parse, validate } =
yogaApp.getEnveloped(ctx);
const args: EnvelopedExecutionArgs = {
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);
});
apply these changes
Mutation: {
addTodo: (_: unknown, { title }: Todo) => {
const newTodo = {
id: "" + (todos.length + 1),
title,
completed: false
};
todos.push(newTodo);
publishToChannel({ todos });
return newTodo;
},
Subscription: {
todos: {
subscribe: () => {
return Repeater.merge(
[
new Repeater(async (push, stop) => {
push({ todos });
await stop;
}),
pubSub.subscribe(TODOS_CHANNEL),
]
)
}
},
},
first, npm i #repeaterjs/repeater then import Repeater

GraphQL Subscriptions is null using Express-GraphQL and graphql-subscriptions

I am using TypeScript and have Server and Client application. Below is the server code.
Server Code
import express, { Express } from "express";
import { graphqlHTTP } from "express-graphql";
import { buildSchema } from "type-graphql";
import { TaskResolver } from "./resolvers/task.resolver";
import { pgDatasource } from "./configs/db.config";
import { SeatBandingResolver } from "./resolvers/seatBanding.resolver";
import { GuestChatResolver } from "./resolvers/guestChat.resolver";
import { RateResolver } from "./resolvers/rate.resolver";
import { YearResolver } from "./resolvers/year.resolver";
import { ImplementationRateResolver } from "./resolvers/implementationRate.resolver";
import { UserResolver } from "./resolvers/user.resolver";
import { ReportResolver } from "./resolvers/report.resolver";
// Subscriptions
const ws = require("ws");
const { useServer } = require("graphql-ws/lib/use/ws");
const { execute, subscribe } = require("graphql");
const main = async () => {
const app: Express = express();
try {
//connect to db
await pgDatasource.initialize();
} catch (err) {
throw err;
}
//build gql schema
let schema = await buildSchema({
resolvers: [
SeatBandingResolver,
GuestChatResolver,
RateResolver,
YearResolver,
ImplementationRateResolver,
UserResolver,
],
validate: false,
// pubSub: new PubSub()
});
let schemaDoc = await buildSchema({
resolvers: [ReportResolver],
validate: false,
});
//ql schema for report
const docServer = graphqlHTTP((req, res) => {
return {
schema: schemaDoc,
graphiql: true,
context: {
req: req,
header: req.headers,
},
};
});
//setting a graphql server instance
const graphqServer = graphqlHTTP((req, res, graphQLParams) => {
return {
schema,
context: {
req: req,
header: req.headers,
},
graphiql: true,
};
});
app.use(cors());
//graphql endpoint : change it to backend
app.use("/graphql", graphqServer);
//for report : change name to google api
app.use("/doc", docServer);
//test route
app.get("/", (req, res) => {
res.json({
message: "Hello world",
});
});
let server = app.listen(3001, () => {
console.log("server started");
const wsServer = new ws.WebSocketServer({
host: "localhost",
// server,
path: "/graphql",
port: 3001,
});
useServer(
{
schema,
execute,
subscribe,
onConnect: (ctx) => {
console.log("Connect");
},
onSubscribe: (ctx, msg) => {
console.log("Subscribe");
},
onNext: (ctx, msg, args, result) => {
console.debug("Next");
},
onError: (ctx, msg, errors) => {
console.error("Error");
},
onComplete: (ctx, msg) => {
console.log("Complete");
},
},
wsServer
);
});
};
//starting a server
main()
.then(async (_) => {
// await addColumn()
})
.catch((err) => {
console.log(err);
});
Subscription Code at Client Side
import { Year } from "../entities/year.entity";
import { NewYear } from "../inputs/addYear.input";
import {
Arg,
Ctx,
Field,
Int,
Mutation,
ObjectType,
Query,
Resolver,
Root,
Subscription,
UseMiddleware,
} from "type-graphql";
import { Request } from "express";
import { Response } from "../helpers/response.helper";
import { Pagination } from "../inputs/pagination.input";
import { isAuth } from "../helpers/auth.helper";
import { PubSub, PubSubEngine } from "graphql-subscriptions";
const pubSub = new PubSub();
#ObjectType()
class MessagePayload {
#Field()
message: string;
}
#Resolver(() => Year)
export class YearResolver {
#Mutation(() => String)
async sendMessage(#Arg("message") message: string): Promise<string> {
console.log("in send subscription");
pubSub.publish("MESSAGE_NOTIFICATION", { message });
return message;
}
//calling the subscription
#Subscription(() => MessagePayload || null, {
topics: "MESSAGE_NOTIFICATION",
})
async receiveMessage(
#Root() root: MessagePayload
): Promise<MessagePayload | null> {
console.log("in publisher");
console.log(root, "in recieve message");
pubSub.asyncIterator("MESSAGE_NOTIFICATION");
return { message: "hello from the subscription" };
}
}
The issue I am facing here is Subscription is not working properly and the data is always null.
Can anyone help me to identify what I am missing here?
Thanks.
I'm not sure for 100% because your code descriptions are kinda confusing, but it looks like you should return pubSub.asyncIterator('MESSAGE_NOTIFICATION') in method receiveMessage. This method is called to start streaming messages to client at selected channel (MESSAGE_NOTIFICATION), not sending them. To send messages use pubsub. Of course you should change typing too.
You can find a similiar implementation here.

How to use Redux to dispatch data to the backend (and consequently mongoDB)?

I recently created a simple MERN application that is supposed to use a form to send data to the backend using Redux to maintain state management. I'm new to Redux (as you will see in my code) and I believe I must have messed up the dispatching.
Below are the functions in my Form component:
const [landlordData, setLandlordData] = useState({name: '', type: '', rating: '', details: ''});
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
console.log(landlordData);
dispatch(createLandlord(landlordData));
}
Which console log the data from the form normally. When I submit though the new entry in the MongoDB only includes the time created and the UUID of the entry due to the Schema of the database:
import mongoose from 'mongoose';
const landlordSchema = mongoose.Schema({
name: String,
type: String,
rating: Number,
details: String,
createdAt: {
type: Date,
default: new Date()
}
});
var landlordDetails = mongoose.model('Landlords', landlordSchema);
export default landlordDetails;
To provide more context on the backend operations here is the controller script I made:
import landlordDetails from '../models/landlords.js';
export const getLandlords = async (req, res) => {
try {
const getDetails = await landlordDetails.find();
console.log(getDetails);
res.status(200).json(getDetails);
} catch (error) {
res.status(404).json({ message: error.message });
}
}
export const createLandlords = async (req, res) => {
const details = req.body;
const newLandlord = new landlordDetails(details);
try {
await newLandlord.save();
res.status(201).json(newLandlord);
console.log("New landlord added!");
} catch (error) {
res.status(409).json({ message: error.message })
}
}
Please let me know if any more information is needed or if I am completely oblivious to something obvious. Thank you.
EDIT: To provide more context, here are my api calls and my action script:
API:
import axios from 'axios';
const url = 'http://localhost:5000/landlords';
export const fetchLandlords = () => axios.get(url);
export const createLandlord = (landlordData) => axios.post(url, landlordData);
Actions JS file:
import * as api from '../api/index.js';
//Action creators
export const getLandlords = () => async (dispatch) => {
try {
const { data } = await api.fetchLandlords();
dispatch({ type: 'FETCH_ALL', payload: data });
} catch (error) {
console.log(error.message);
}
};
export const createLandlord = (landlord) => async (dispatch) => {
try {
const { data } = await api.createLandlord(landlord);
dispatch({ type: 'CREATE', payload: data });
} catch (error){
console.log(error);
}
};
When I click the submit button, a new database entry is made with the createdAt field but nothing else.

Apollo GraphQL v3 subscription returning null

I'm having a super hard time with setting up subscriptions in apollo-server. It's listening, I can make the mutation and query the result. But, the subscription for the same data returns null every time. Here are the important parts of my code:
import { Storage } from "#google-cloud/storage"
import { createError } from "apollo-errors"
import { ApolloServer, gql, PubSubEngine } from "apollo-server-express"
import { PubSub } from "apollo-server"
import cors from "cors"
import express from "express"
import jwt from "express-jwt"
import jwksRsa from "jwks-rsa"
import neo4j from "neo4j-driver"
import { initializeDatabase } from "./initialize"
import { typeDefs } from "./schema.js"
import { createServer } from "HTTP"
const pubsub = new PubSub()
const app = express()
const messages = []
const resolvers = {
Mutation: {
postMessage(parent, { user, content }, context) {
const id = messages.length
messages.push({
id,
user,
content,
})
pubsub.publish("test", {
messages,
})
return id
},
},
Query: {
messages: () => messages,
},
Subscription: {
messages: {
subscribe: (parent, args, context) => {
return pubsub.asyncIterator("test")
},
},
},
}
const neoSchema = new Neo4jGraphQL({
typeDefs,
resolvers,
config: {
jwt: {
secret: process.env.NEO4J_GRAPHQL_JWT_SECRET,
},
},
debug: true,
})
const NEO4J_USER = process.env.NEO4J_USER
const NEO4J_PASS = process.env.NEO4J_PASS
const NEO4J_URI = process.env.NEO4J_URI
const driver = neo4j.driver(NEO4J_URI, neo4j.auth.basic(NEO4J_USER, NEO4J_PASS))
const init = async (driver) => {
await initializeDatabase(driver)
}
try {
init(driver).catch((e) => console.log(e))
console.log("ne4j initialized...")
} catch (error) {
console.error(`Failed to property initialize database`, error)
}
const apolloServer = new ApolloServer({
schema: neoSchema.schema,
context: ({ req }) => {
return {
driver,
pubsub,
req,
}
},
subscriptions: {
onConnect: async (connectionParams, webSocket, context) => {
console.log("xxx")
console.log(connectionParams)
},
onDisconnect: (websocket, context) => {
console.log("WS Disconnected!")
},
path: "/graph",
},
introspection: true,
playground: true,
})
apolloServer.applyMiddleware({ app, path: "/graph" })
const httpServer = createServer(app)
apolloServer.installSubscriptionHandlers(httpServer)
const port = process.env.PORT || 8080
httpServer.listen({ port }, () => {
console.log(`Server is ready at http://localhost:${port}${apolloServer.graphqlPath}`)
console.log(`Subscriptions ready at ws://localhost:${port}${apolloServer.subscriptionsPath}`)
})
And from my schema:
export const typeDefs = gql`
type Message {
id: ID!
user: String!
content: String!
}
type Query {
messages: [Message!]
}
type Subscription {
messages: [Message!]
}
type Mutation {
postMessage(user: String!, content: String!): ID!
}
`
If I execute a mutation on postMessage, then query messages, I get the expected data back. However, when I run the subscription from the client, I get
{
"data": {
"messages": null
}
}
What's happening here? What am I missing? Is there something missing in my server setup? Are my pubsub and subscriptions code off?

TypeError: Cannot read property 'shawarmaOrdered' of undefined

I am new to GraphQL and mongoose and trying to create a function in a resolver which creates a shawarma order, the inputs required are the shawarma ID, the quantity and an address, when I run the code typed below, I get an Error which states
TypeError: Cannot read property 'shawarmaOrdered' of undefined
the resolver code:
import mongoose from 'mongoose';
import AuthenticationError from 'apollo-server';
import {shawarma} from '../models/shawarmaModel';
export default {
Query:{
},
Mutation: {
createOrder: async(parent, {OrderInput}, {models: {orderModel, shawarmaModel}}, info) => {
console.log('reached1')
try{
const {shawarmaOrdered, quantity, address} = OrderInput;
const order = await orderModel.updateOne({shawarmaOrdered, quantity, address})
return order
} catch(err){
console.log('errorr')
console.log(err)
}
}
},
Order: {
shawarmaOrdered: async(parent, {shawarmaOrdered}, {models:shawarmaModel}, info) =>{
return shawarmaOrdered = shawarmaModel.find((shawarma)=>{
return shawarma.id == parent.id
})
}
}
the schema code:
import {gql} from 'apollo-server'
export default gql`
type Order{
id: ID!
shawarmaOrdered: Shawarma!
quantity: Int
orderedTo: String
}
input OrderInput{
shawarmaOrdered: ID!
quantity: Int
orderedTo: String
}
extend type Query {
order(id: ID!): Order!
}
extend type Mutation {
createOrder(shawarmaOrdered: String!, quantity: Int!, orderedTo: String!): Order!
}
`
the order model code:
import mongoose, { Mongoose } from 'mongoose'
import shawarma from './shawarmaModel'
const orderSchema = new mongoose.Schema({
shawarmaOrdered: {
type: mongoose.Schema.Types.ObjectId,
ref: shawarma
},
quantity: {
type: Number
},
completed: {
type: Boolean,
default: false
}
})
const order = mongoose.model('order', orderSchema)
export default order;
kindly let me know if I'm doing something wrong
So here's how I fixed the problem.
in the index.js file
import cors from 'cors';
import express from 'express';
import jwt from 'jsonwebtoken';
import mongoose from 'mongoose';
import { ApolloServer, AuthenticationError } from 'apollo-server-express';
import schemas from './schemas';
import resolvers from './resolvers';
import userModel from './models/userModel';
import orderModel from './models/orderModel';
import shawarmaModel from './models/shawarmaModel';
const app = express();
app.use(cors());
const getUser = async (req) => {
const token = req.headers['token'];
// console.log(token)
if (token) {
try {
return await jwt.verify(token, 'riddlemethis');
} catch (e) {
console.log(e)
throw new AuthenticationError('Your session expired. Sign in again.');
}
}
};
const server = new ApolloServer({
typeDefs: schemas,
resolvers,
context: async ({ req }) => {
if (req) {
const me = await getUser(req);
return {
me,
models: {
userModel,
orderModel, //this was where the orderModel was misspelled as OrderModel
shawarmaModel
},
};
}
},
});
server.applyMiddleware({ app, path: '/graphql' });
app.listen(5000, async () => {
await mongoose.connect('mongodbconnectionString')
});
in the code above, I have commented the section in which the error was from. Carelessly, when debugging I overlooked checking this file. my mistake. I apologise for not including this code in the original question

Resources