Good day, I am trying to make a simple app where , when user add any product subscriber will trigger and event and send back the product.I've implemented some code with help of google everything is working excepts subscriber, When I browse graphiql in my browser I see some error like Expected { query: { kind: "Document", definitions: [Array], loc: [Object] }, mutation: { Query: [Object], Mutation: [Object], Subscription: [Object], Upload: Upload } } to be a GraphQL schema.
, Here is my code below ,
Server main index.js file
const express = require('express')
const morgan = require('morgan')
const cors = require('cors')
const bodyParser = require('body-parser')
const {ApolloServer, gql} = require('apollo-server-express');
const {createServer} = require('http');
const {execute, subscribe} = require('graphql');
const {makeExecutableSchema} = require('#graphql-tools/schema');
const {PubSub} = require('graphql-subscriptions');
const {SubscriptionServer} = require('subscriptions-transport-ws');
const typeDefs = require("./GraphQLSchema/typeDef")
const resolvers = require("./GraphQLSchema/resolvers")
const myGraphQLSchema = require('./GraphQLSchema');
const coreRouting = require("./routes")
require('dotenv').config()
const app = express()
app.use(bodyParser.json())
//app middleware
app.use(morgan('dev'));
//app.use(cors())
const {PrismaClient} = require("#prisma/client")
const prisma = new PrismaClient();
async function checkPrismaConnection() {
const obj = {
include: {transactions: true}
}
try {
// const result = await prisma.product.findMany({
// include: {transactions: false}
// });
const result = await prisma.transaction.findMany();
console.log(result);
} catch (e) {
console.log(e);
}
}
//checkPrismaConnection();
app.use(coreRouting);
app.get('/test/route', (req, res) => res.send('Hello World!'))
const serverPort = process.env.PORT || 9000
app.use('/graphql', bodyParser.json());
const apolloServer = new ApolloServer(
{
typeDefs,
resolvers,
context: ({req}) => ({req, pubsub})
});
apolloServer.applyMiddleware({app});
const pubsub = new PubSub();
const server = createServer(app);
//const schema = makeExecutableSchema({typeDefs, resolvers});
server.listen(serverPort, () => {
new SubscriptionServer({
execute,
subscribe,
pubsub,
schema: {
query: typeDefs,
mutation: resolvers,
},
graphiql: true
}, {
server: server,
path: '/graphql',
});
});
typeDefs Code
const {gql} = require("apollo-server-express");
module.exports = gql`
type Post{
body: String!
}
type Product{
id: Int!
name: String!
transactions:[Transaction]!
}
type Transaction{
id: Int!
quantity: Int!
time: String!
}
input ProductInput{
name: String!
}
type Query {
getPosts: [Post]
products: [Product]
product(id: ID!): Product
}
type Mutation {
createProduct(productInput: ProductInput):Product!
}
type Subscription {
newProduct: Product!
}
`
Resolvers Code
const {helperMethodForFetcingProducts, helperMethodForCreateProduct} = require("../../helper");
const {relationalKeyword} = require("../../helper/keyword");
module.exports = {
Query: {
getPosts() {
return [{"body": "First Post"}, {"body": "2nd Post"}, {"body": "3rd Post"}]
},
products: async function () {
return helperMethodForFetcingProducts(relationalKeyword, false);
},
product: async function (_, {id}) {
console.log(_, id);
return helperMethodForFetcingProducts(relationalKeyword, true, parseInt(id));
}
},
Mutation: {
createProduct: async (_, {productInput: {name}}, {pubsub}) => {
let getProductFromMethod = await helperMethodForCreateProduct(name);
pubsub.publish('Proudct_Added', {
newProduct: getProductFromMethod
})
return getProductFromMethod;
}
},
Subscription: {
newProduct: {
subscribe: (_, __, {pubsub}) => pubsub.asyncIterator('Proudct_Added')
}
}
}
My Error
Since I am new in graphQL I don't understand the issue actually.
Related
I am trying to mutate in GraphQL to create a new user but I get the error of
Cannot return null for non-nullable field Mutation.register.
Screenshot of operation, variable and response here.
graphql/schema/schema.js
const{ buildSchema } = require('graphql');
const schema = buildSchema(`
type User {
_id: ID!
email: String!
username: String!
token: String!
createdAt: String!
}
input RegisterInput{
username: String!
password: String!
confirmPassword: String!
email: String!
}
type Query {
user(id: ID!): User
}
type Mutation {
register(registerInput: RegisterInput): User!
}
schema {
query: Query
mutation: Mutation
}
`)
module.exports = schema;
graphql/resolvers/user.js
const User = require('../../models/user.js');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const SECRET_KEY = 'oPXA96op!u%,`:}eT^.!|hvXohA~fa';
module.exports = {
Mutation: {
async register(
_,
{
registerInput: { username, email, password, confirmPassword}
},
context,
info
) {
password = await bcrypt.hash(password, 12);
const newUser = new User({
email,
username,
password,
createdAt: new Date().toISOString
});
const res = await newUser.save();
const token = jwt.sign({
id: res.id,
email: res.email,
username: res.username
}, SECRET_KEY, { expiresIn: '1h'});
return{
...res._doc,
id: res._id,
token
};
}
}
};
graphql/resolvers/index.js
const postResolver = require('./post');
const userResolver = require('./user');
const resolvers = {
Mutation: {
...userResolver.Mutation
},
...postResolver
}
module.exports = resolvers;
server.js
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const mongoose = require('mongoose');
const cors = require('cors');
const { graphqlHTTP } = require('express-graphql');
const MONGODB = '';
const schema = require('./graphql/schema');
const resolvers = require('./graphql/resolvers');
const PORT = 4000;
const server = new ApolloServer({ schema, resolvers });
mongoose.connect(MONGODB);
mongoose.connection.once('open', function() {
console.log('Connected to the Database.');
});
mongoose.connection.on('error', function(error) {
console.log('Mongoose Connection Error : ' + error);
});
const app = express();
app.use(cors());
let apolloServer = null;
async function startServer() {
apolloServer = new ApolloServer({
schema,
resolvers,
});
await apolloServer.start();
apolloServer.applyMiddleware({ app });
}
startServer();
app.use("/graphql", graphqlHTTP({
schema: schema,
rootValue: resolvers,
graphiql: true
}));
app.listen(PORT, function() {
console.log(`Server listening on port ${PORT}.`);
});
I have checked many previous posts about this mutation error but none have seen to be resolved, and also have done many modifications to the schema/resolver but has not worked. I have also posted on the GraphQL Discord for help, but have had no luck. I tried changing the server.js but it affected my queries from running so I reverted.
/graphql/resolvers/post.js
const Post = require('../../models/post.js');
const { GraphQLDateTime } = require ('graphql-iso-date');
const customScalarResolver = {
Date: GraphQLDateTime
};
function posts() {
return Post.find({});
}
function post(args) {
return Post.findById(args.id)
}
function createPost(args) {
let post = new Post(args.postInput);
return post.save();
}
function deletePost(args) {
return Post.findByIdAndRemove(args.id);
}
function updatePost(args) {
return Post.findByIdAndUpdate(args.id, args.postInput, { new: true });
}
module.exports = { posts, post, createPost, deletePost, updatePost, customScalarResolver}
I don't get the request variable in the mutation GraphQL on the backend. I don't understand why it doesn't work.
I get the next error:
"Cannot destructure property 'name' of 'undefined' as it is undefined."
That mutation I make in Apollo Studio:
mutation Mutation($createOwnerName: String) {
createOwner(name: $createOwnerName)
}
My variables in Apollo Studio:
{
"createOwnerName": "John"
}
My backend with Express
schema.js:
const { buildSchema } = require("graphql");
const schema = buildSchema(`
type Mutation {
createOwner(name: String): String
}
`);
module.exports = schema;
resolvers.js:
const resolvers = {
Mutation: {
createOwner: ({name}) => {
console.log('createOwner name', name)
return name
}
}
}
server.js:
const { createServer } = require("http");
const express = require("express");
const { execute, subscribe } = require("graphql");
const { ApolloServer } = require("apollo-server-express");
const { SubscriptionServer } = require("subscriptions-transport-ws");
const { makeExecutableSchema } = require("#graphql-tools/schema");
const typeDefs = require("./graphql/schema.js");
const resolvers = require("./graphql/resolvers.js");
require("dotenv").config();
const mongoose = require("mongoose");
// mongoose
mongoose
.connect(process.env.DB_HOST, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("MongoDB connected"))
.catch((err) => console.log(err));
(async () => {
const PORT = 3033;
const app = express();
const httpServer = createServer(app);
app.get("/rest", function (req, res) {
return res.json({ data: "rest" });
});
const schema = makeExecutableSchema({ typeDefs, resolvers });
const server = new ApolloServer({
schema,
});
await server.start();
server.applyMiddleware({ app });
SubscriptionServer.create(
{ schema, execute, subscribe },
{ server: httpServer, path: server.graphqlPath }
);
httpServer.listen(PORT, () => {
console.log(
`🚀 Query endpoint ready at http://localhost:${PORT}${server.graphqlPath}`
);
console.log(
`🚀 Subscription endpoint ready at ws://localhost:${PORT}${server.graphqlPath}`
);
});
})();
You are destructuring the wrong argument. Arguments are in that order:
Parent value
Argument values
Context
GraphQL Resolve Info
Destructure the second parameter:
const resolvers = {
Mutation: {
createOwner: (parent, {name}) => {
console.log('createOwner name', name)
return name
}
}
}
I am trying to pass an array of schemas that will populate multiple social media documents in the qrCode document but when I send the post request using Postman it only sends 1 of them.
This is the QrCode Modle where the shcema is being defined
const Joi = require("joi");
const mongoose = require("mongoose");
const { themeSchema } = require("./Theme");
const { userSchema } = require("./User");
const { socialSchema } = require("./Social");
const QrCode = mongoose.model(
"QrCode",
new mongoose.Schema({
user: {
type: userSchema,
required: true,
},
name: {
type: String,
maxLength: 255,
required: true,
trim: true,
},
theme: {
type: themeSchema,
required: true,
},
// Social Media Links
social: [
{
type: socialSchema,
required: true,
},
],
})
);
function ValidateQrCode(qrCode) {
const schema = {
userId: Joi.objectId(),
name: Joi.string().max(255).required(),
themeId: Joi.objectId().required(),
socialId: Joi.array().required().items(Joi.string()),
};
return Joi.validate(qrCode, schema);
}
module.exports.QrCode = QrCode;
module.exports.validate = ValidateQrCode;
This is the post route to create a new qrCode
const { QrCode, validate } = require("../models/QrCode");
const { Theme } = require("../models/Theme");
const { User } = require("../models/User");
const { Social } = require("../models/Social");
const auth = require("../middleware/auth");
const express = require("express");
const router = express.Router();
router.get("/", async (req, res) => {
const qrCodes = await QrCode.find().sort("-name");
res.send(qrCodes);
});
router.post("/", auth, async (req, res) => {
const { error } = validate(req.body);
if (error) res.status(400).send(error.details[0].message);
const theme = await Theme.findById(req.body.themeId);
if (!theme) return res.status(400).send("Invalid theme.");
const social = await Social.findById(req.body.socialId);
if (!social) return res.status(400).send("Invalid social.");
const user = await User.findById(req.user._id);
if (!user) return res.status(400).send("Invalid theme.");
const qrCode = new QrCode({
user: user,
name: req.body.name,
theme: theme,
social: social,
});
await qrCode.save();
res.send(qrCode);
});
module.exports = router;
This is the postman post request that I send
{
"name": "Test399",
"themeId": "60f607ab97943dfaa05811bc",
//the ID's for all the socials
"socialId": ["60f657f97f90bb0cd10cfef1", "60f77d179b05d91894ef32ab"]
}
Your route has
const social = await Social.findById(req.body.socialId);
here you are trying to pass array and Mongoose findById document says to send the _id for query, that is the reason mongoose gives only one value.
Iterate over the array and get values before sending the response.
I try to create category to eCommerce project then it throws an error
Postman - throws an error
These are source codes
location: ecommerce-backend\index.js
const express = require('express')
const env = require('dotenv')
const app = express()
const mongoose = require('mongoose')
//routes
const authRoutes = require('./routes/auth')
const adminRoutes = require('./routes/admin/auth')
const categoryRoutes = require('./routes/category')
const productRoutes = require('./routes/product')
const cartRoutes = require('./routes/cart')
//environment variable or you can say constants
env.config()
//mongodb connection
mongoose.connect(
`mongodb+srv://${process.env.MONGO_DB_USERS}:${process.env.MONGO_DB_PASSWORD}#cluster0.nglbc.mongodb.net/${process.env.MONGO_DB_DATABASE}?retryWrites=true&w=majority`,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
}
).then(() => {
console.log('Database connected')
});
app.use(express.json())
app.use('/api', authRoutes)
app.use('/api', adminRoutes)
app.use('/api', categoryRoutes)
app.use('/api', cartRoutes)
app.use('/api', productRoutes)
app.listen(process.env.PORT, () => {
console.log(`Server is running on port ${process.env.PORT}`)
})
location: ecommerce-backend\routes\category.js
const express = require('express')
const { requireSignin, adminMiddleware } = require('../common-middleware')
const { addCategory,getCategories } = require('../controller/category')
const router = express.Router()
const path = require('path')
const shortid = require('shortid')
const multer = require('multer')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(path.dirname(__dirname), 'uploads'))
},
filename: function (req, file, cb) {
cb(null, shortid.generate() + '-' + file.originalname)
}
})
const upload = multer({ storage })
router.post('/category/create',requireSignin, adminMiddleware,upload.single('categoryImage'), addCategory)
router.get('/category/getcategory', getCategories)
module.exports = router
location: ecommerce-backend\models\category.js
const mongoose = require('mongoose')
const categorySchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
slug: {
type: String,
required: true,
unique: true
},
categoryImage: {
type: String,
},
parentId: {
type: String
}
}, { timestamps: true})
module.exports = mongoose.model('Category',categorySchema)
location: ecommerce-backend\controller\category.js
const Category = require('../models/category')
const slugify = require('slugify')
function createCategories(categories, parentId = null){
const categoryList = []
let category
if(parentId == null){
category = categories.filter(cat => cat.parentId == undefined)
}else{
category = categories.filter(cat => cat.parentId == parentId)
}
for(let cate of category){
categoryList.push({
_id: cate._id,
name: cate.name,
slug: cate.slug,
children: createCategories(categories,cate._id)
})
}
return categoryList
}
exports.addCategory = (req, res) => {
const categoryObj = {
name: req.body.name,
slug: slugify(req.body.name)
}
if(req.file){
categoryObj.categoryImage = process.env.API + '/public/'+ req.file.filename
}
if(req.body.parentId){
categoryObj.parentId = req.body.parentId
}
const cat = new Category(categoryObj)
cat.save((error,category) => {
if(error) return res.status(400).json({ error})
if(category){
return res.status(201).json({ category})
}
})
}
exports.getCategories = (req,res) => {
Category.find({})
.exec((error, categories) => {
if(error) return res.status(400).json({error})
if(categories){
const categoryList = createCategories(categories)
res.status(200).json({categoryList})
}
})
}
this is my .env file at ecommerce-backend\ .env
PORT = 2000
MONGO_DB_USERS = mrzombit
MONGO_DB_PASSWORD = ********
MONGO_DB_DATABASE = ecommerce
JWT_SECRET = MERNSECRET
API = http://localhost:2000
I face this problem then I can't figure it out what happened to my code
Thank you!
Make sure you have change the 'Content-Type' in postman header section.
Content-Type: multipart/form-data; boundary=<calculated when request is sent>
I just do below steps:
Delete slugify package from package.json
Reinstall slugify package : you will see that
found 2 high severity vulnerabilities
run npm audit fix to fix them, or npm audit for details
Run npm audit fix
Open new window ! in postman and
copy the token from /api/admin/create and paste this token in the new window: /api/category/create in body ,
form-data :
name (doesn't exist in your DB yet)
categoryImage (click file not text)
You can also try with the following code which I hope would work for you.
**slug: slugify(toString(req.body.name))**
Add
slug: { type: String, slug: "title"} to your model.
I tried to debug the problem of slugify: string argument expected & found that in my case this object is comeing as {} so it was throwing slugify: string argument expected.
try to find if all values are properly received in slugify method.
Code snippet
Schema.pre('save', (next)=> {
console.log(`pre hook is triggered ${this.name}`.silly);
// this.set({ updatedAt: new Date() });
this.slug = slugify(this.name,{lower:true})
next()
})
code is supposed to return a a JSON object with empty tasks when requesting /tasks instead it returns a message error- TypeError: cannot read property 'findAll' of undefined. The source of errors as per the message comes from routes >tasks.js see below for screenshots or/and live code on sandbox.
Project Folder:
sandbox
some codes:
src>models >tasks.js
module.exports = (sequelize, DataType) => {
const Tasks = sequelize.define(
"Tasks",
{
id: {
type: DataType.INTEGER,
primaryKey: true,
autoIncrement: true
},
title: {
type: DataType.STRING,
allowNull: false,
validate: {
notEmpty: true
}
},
done: {
type: DataType.BOOLEAN,
allowNull: false,
defaultValue: false
}
},
{
classMethods: {
associate: models => {
Tasks.belongsTo(models.Users);
}
}
}
);
return Tasks;
};
src>routes>tasks.js
module.exports = app => {
const Tasks = app.db.models.tasks;
app.get("/tasks", (req, res) => {
Tasks.findAll({}).then(tasks => {//source of error as per error message
res.json({ tasks: tasks });
});
});
};
src >db.js
var path = require("path");
var fs = require("fs");
var Sequelize = require("sequelize");
//const config = require("./libs/config.js");
var sequelize = null;
let db = null;
module.exports = app => {
if (!db) {
const config = app.libs.config;
sequelize = new Sequelize(
config.database,
config.username,
config.password,
config.params
);
db = {
sequelize,
Sequelize,
models: {}
};
const dir = path.join(__dirname, "models");
fs.readdirSync(dir).forEach(file => {
const modelDir = path.join(dir, file);
const model = sequelize.import(modelDir);
db.models[model.name] = model;
});
Object.keys(db.models).forEach(key => {
db.models[key].options.classMethods.associate(db.models);
});
}
return db;
};
src>index.js
var express = require("express");
var consign = require("consign");
var app = express();
consign({ cwd: "src" })
.include("./libs/config.js")
.then("db.js")
.then("./libs")
.then("./routes")
.into(app);
On file routes/tasks.js line 2, add a capital on task;
const Tasks = app.db.models.Tasks;
Then it should works.