How to get the grandparent data in resolver graphql? - rest

I'm new to GraphQL. I am wrapping a REST API wiht GraphQL, my problem is that I don't get the
ingredients of restaurant dishes (grandparents), just get the ingredients of a dish (parents) .This is my schema
type Restaurant{
id_restaurant: String
name: String
dishes:[Dish]
}
type Dish{
id_dish: String
name: String
ingredients:[Ingredient]
}
type Ingredient{
id_ingredient: String
name: String
}
type Query{
restaurants: [Restaurant]
dishes: [Dish]
ingredients: [Ingredient]
}
These are my resolvers
const mainURL = `https://my_apy_example`
const resolvers = {
Query: {
restaurants: () => { return fetch(`${mainURL}/restaurants`).then(res => res.json()) },
dishes: () => { return fetch(`${mainURL}/dishes`).then(res => res.json()) },
ingredients: () => { return fetch(`${mainURL}/ingredients`).then(res => res.json()) },
},
Restaurant: {
dishes: parent => {
const { id_restaurant } = parent
return fetch(`${mainURL}/dishes?id_restaurant=${id_restaurant}`).then(res => res.json())
},
},
Dish: {
ingredients: parent => {
const { id_dish } = parent
return fetch(`${mainURL}/ingredients?id_dish=${id_dish}`).then(res => res.json())
},
},
}
I assumed that the Dish resolver also resolved the dishes of Restaurant but I think I need other resolver for this, I was wrong.
This query doesn't show the ingredients
query{
restaurants{
name
dishes{
name
ingredients{
name
}
}
}
}
This query shows the ingredients
query{
dishes{
name
ingredients{
name
}
}
}
}
Any solution?

Related

PrismaClientValidationError: Missing required argument in connectOrCreate

Problem:When I try and send/store data in my database I get this error. Specifically, I am trying to create/save a classroom with student names.
Tech Used:
Prisma/Postgres connected to AWS RDS and Next.js, deployed on Vercel, etc.
Error Message
PrismaClientValidationError: Argument data.classrooms.upsert.0.create.students.connectOrCreate.0.create.school.connect of type schoolWhereUniqueInput needs at least one argument.
Argument data.classrooms.upsert.0.update.students.upsert.0.create.school.connect of type schoolWhereUniqueInput needs at least one argument.
at Document.validate (/var/task/node_modules/#prisma/client/runtime/index.js:29501:20)
at serializationFn (/var/task/node_modules/#prisma/client/runtime/index.js:33060:19)
at runInChildSpan (/var/task/node_modules/#prisma/client/runtime/index.js:22550:12)
at PrismaClient._executeRequest (/var/task/node_modules/#prisma/client/runtime/index.js:33067:31)
at async PrismaClient._request (/var/task/node_modules/#prisma/client/runtime/index.js:32994:16)
at async profile (/var/task/.next/server/pages/api/user/profile.js:175:27)
at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils/node.js:366:9)
at async NextNodeServer.runApi (/var/task/node_modules/next/dist/server/next-server.js:481:9)
at async Object.fn (/var/task/node_modules/next/dist/server/next-server.js:735:37)
at async Router.execute (/var/task/node_modules/next/dist/server/router.js:247:36) {
clientVersion: '4.9.0'
}
DB Models with relationships: school (1 to many w/students); students (many to many with classrooms); teachers (one to many with students, many to many with classrooms)
Code/Prisma Query
export default async (req, res) => {
...
classroom.students.forEach((student) => {
const totalStudentPoints = student.rewardsRecieved.reduce(
(totalPoints, reward) => {
return totalPoints + reward.pointValue;
},
0
);
groups[student.group.name] += totalStudentPoints;
});
return { ...classroom, groupsTotalPoints: groups };
});
user.classrooms = newClassrooms;
res.json(user);
} else {
console.log("Could Not Find User");
res.status(401).json({
error: "Not authorized",
});
}
}
if (req.method === "PUT") {
const connectStudents = (shouldUpsert) => {
const students = req.body.students;
return students.map((student) => {
const UNSAFEHASH = md5(student.id);
const studentQuery: any = {
where: {
id: student.id,
},
create: {
id: student.id,
firstName: student.firstName,
lastName: student.lastName,
profilePicture: student.profilePicture,
userKey: UNSAFEHASH,
school: {
connect: {
id: req.body.schoolId,
},
},
group: {
connect: {
id: student.group.id,
},
},
},
};
if (shouldUpsert) {
studentQuery.update = {
firstName: student.firstName,
lastName: student.lastName,
profilePicture: student.profilePicture,
userKey: UNSAFEHASH,
group: {
connect: {
id: student.group.id,
},
},
};
}
return studentQuery;
});
};
try {
const user = await prisma.staff.update({
where: {
id: session.id,
},
data: {
firstName: req.body.firstName,
lastName: req.body.lastName,
classrooms: {
upsert: [
{
where: {
id: req.body.classId || "-1",
},
create: {
// id: req.body.classId,
name: req.body.className,
subject: req.body.classSubject,
students: {
connectOrCreate: connectStudents(false),
},
},
update: {
name: req.body.className,
subject: req.body.classSubject,
students: {
upsert: connectStudents(true),
},
},
},
],
},
},
});
Take a look at the PUT request and the prima.staff.update method more specifically. I was looking at the UPSERT I have there, but I can't figure out what's wrong.

rxjs expand operator with switchMap

I have a list of students, for each student, I have to access the address. I will get the student list in pagination. so I used the 'expand' operator to get all students. but I am not able to get the address for each student.
Student List
{
students: [
{
id: 1,
name: 'ABC'
},
{
id: 2,
name: 'XYZ'
}
],
nextPageToken: 'Aqw12'
}
Now using the student's id I have to fetch the address.
Here is my sample code to get the student's list with the rxjs expand operator.
const connection = this.searchStudent(nextPageToken).pipe(
expand((students: StudentsDto) => {
if (students.nextPageToken){
nextPageToken = students.nextPageToken;
return this.searchStudent(nextPageToken);
}else{
return new Observable as Observable<StudentsDto>;
}
})
);
const subscription = connection.subscribe({
next(response: StudentsDto) {
response.students.forEach(async (student: StudentDto) => {
console.log(student);
});
},
error(e) {
console.log(e)
},
complete() {
subscription.unsubscribe();
},
});
Now I am not able to get, what operator should I apply to get the address.
for address, I have implemented API in this.getAddress(studentId) function which return Observables.
Please help,
Thanks.
const connection = this.searchStudent(nextPageToken).pipe(
expand((response: StudentsDto) => {
if (response.nextPageToken){
nextPageToken = response.nextPageToken;
return this.searchStudent(nextPageToken);
}else{
return new Observable as Observable<StudentsDto>;
}
}),
mergeMap((response: StudentsDto) => {
return response.students.map(student => this.getAddress(student.id))
}),
mergeAll(),
);
const subscription = connection.subscribe({
next(response: AddressDto) {
// Perform/save operation on Address object
},
error(e) {
console.log(e)
},
complete() {
subscription.unsubscribe();
},
});

GraphQL Mongoose: Cast to ObjectId failed for value

I have the following resolver for GraphQL:
const Post = require("../../models/Post");
module.exports = {
getAllActivePosts: async (userId) => {
try {
const posts = await Post.find({
userId: userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
return posts;
} catch (err) {
console.log(err);
throw err;
}
},
};
which tries to get all active posts by the ID of the user from the Post model:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const PostSchema = new mongoose.Schema({
userId: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
content: {
type: String,
required: true,
},
createdAt: {
type: Date,
required: true,
}
});
module.exports = Post = mongoose.model("Post", PostSchema);
Here's the GraphQL Schema:
const { buildSchema } = require('graphql');
module.exports = buildSchema(`
type User {
_id: MongoId!
email: String!
password: String
}
type Post {
_id: MongoId!
userId: MongoId!
content: String!
createdAt: String!
}
scalar MongoId
input LoginInput {
email: String!
password: String!
}
type RootQuery {
login(email: String!, password: String!): AuthData!
getAllActivePosts(userId: MongoId!): [Post]
}
type RootMutation {
createUser(loginInput: LoginInput): AuthData!
}
schema {
query: RootQuery
mutation: RootMutation
}
`);
... and the GraphQL query I'm running in GraphiQL:
{
getAllActivePosts(userId: "5fbfc92312b90071179a160f") {
name
createdAt
}
}
For this, the result of the query is:
{
"errors": [
{
"message": "Cast to ObjectId failed for value \"{ userId: '5fbfc92312b90071179a160f' }\" at path \"userId\" for model \"Post\"",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getAllActivePosts"
]
}
],
"data": {
"getAllActivePosts": null
}
}
Searched here for similar issues, tried wrapping userId in ObjectId, but nothing helped. What am I missing here?
I was go through this problem once a year ago with no solution till i get main concept of graphql.
Here you are passing string
{
getAllActivePosts(userId: "5fbfc92312b90071179a160f") {
name
createdAt
}
}
and graphql expecting to have mongoose.Types.ObjectId
getAllActivePosts(userId: MongoId!): [Post]
You need to do sync like
getAllActivePosts(userId: mongoose.Types.ObjectId("5fbfc92312b90071179a160f")) {
But using above way you are not eligible for run query in graphiQL becuse there is no mongoose defined.
type RootQuery {
login(email: String!, password: String!): AuthData!
getAllActivePosts(userId: String!): [Post]
}
Better solution is use userId input as string and then validate on your resolver function like
getAllActivePosts: async ({ userId }) => {
try {
if(mongoose.Types.ObjectId.isValid(userId)) {
const posts = await Post.find({
userId: userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
// you can;t return null you need to return array
return posts ? posts : []
} else {
// if mongoose id is wrong
return []
}
} catch(error) {
// it is better to throw error return blank array to complete flow
throw error
}
}
Turned out, I was using userId directly, whereas I should've used args.userId. The proper resolver below:
module.exports = {
getAllActivePosts: async (args) => {
try {
const posts = await Post.find({
userId: args.userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
return posts;
} catch (err) {
console.log(err);
throw err;
}
},
};
and for the schema:
getAllActivePosts(userId: String!): [Post]

mongodb model contain changed on calling a function levelQuestion

Here is the code for schema
schema stores questions for a particular course and which contains chapter and there question
questions: [
{
Chapter: String,
chques: [
{
description: String,
questions: [
{
question: String,
options: [String],
answer: Number,
Explanation: String,
code: { type: String, default: null },
images: [{ type: String, default: null }],
level: String
}
]
}
]
}
],
Here is the code for route
Router.get(
"/queformock/:course_id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
Courses.findOne({ _id: req.params.course_id })
.lean()
.exec()
.then(course => {
if (!course) res.status(404).json({ course: "course not found" });
else {
let coursepass = Object.assign({}, course);
console.log("coursepass1: ", coursepass); //before calling levelQuestion it output as expected
let level2 = levelQuestion(coursepass, "medium");
console.log("coursepass2: ", coursepass);
let level3 = levelQuestion(coursepass, "hard");
console.log("coursepass3: ", coursepass);
res.json(level2);
}
});
}
);
Here is the levelQuestion function code
function levelQuestion(coursed, type) {
let arr = [];
coursed.questions.forEach(item => {
item.chques.forEach(i => arr.unshift(i));
});
arr.forEach(item => {
_.remove(item.questions, i => {
return i.level !== type;
});
});
_.remove(arr, item => {
return item.questions == false;
});
return arr;
}
now the problem is on every function call of levelQuestion coursepass is changed...why??

Null response in query for Relay Modern interogation on GraphQL

Maybe someone who has managed to pass this step is willing to provide some indications.
I have a schema, a resolver, i request the query and i have a null response.
Please can you help on topic?
module.exports = {
Query: {
allLinks: async (root, {filter}, {mongo: {Links, Users}}) => {
let query = filter ? {$or: buildFilters(filter)} : {};
return await Links.find(query).toArray();
}
and the query request looks like this:
query LinkListPageQuery {
allLinks {
...LinkList_allLinks
}
}
fragment LinkList_allLinks on LinkConnection {
edges {
cursor
...Link_link
}
}
fragment Link_link on LinkEdge {
node {
id
description
url
}
}
My schema looks like this:
const typeDefs = `
type Link implements Node {
id: ID!
url: String!
description: String!
postedBy: User
votes: [Vote!]!
}
interface Node {
id: ID!
}
type Query {
allLinks(filter: LinkFilter, first: Int): [LinkConnection]
node(
id: ID!
): Node
}
type LinkEdge {
node: Link!
cursor: String
}
type LinkConnection {
pageInfo: PageInfo
edges: LinkEdge
count: Int
}
input LinkFilter {
OR: [LinkFilter!]
description_contains: String
url_contains: String
}
}
`;
PS: This language schema is done according to graphql-tools package.
Resolver:
Query: {
users: async (root, { first, after }, { mongo: { Users }, user }) => {
const queryData = await Users.find(query).toArray();
first = first || queryData.length;
after = after ? parseInt(fromCursor(after), 10) : 0;
const edges = queryData.map((node, i) => ({
cursor: toCursor(i+1),
node: node._id,
})).slice(after, first + after);
const slicedUser = edges.map(({ node }) => node);
return {
edges,
pageInfo: {
startCursor: edges.length > 0 ? edges[0].cursor : null,
hasNextPage: first + after < queryData.length,
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : null
},
count: queryData.length,
};
},
UserConnection: {
edges: ({ edges }) => edges,
pageInfo: ({ pageInfo }) => pageInfo,
count: ({ count }) => count,
},
UserEdge: {
node: async ({ node },data, {dataloaders: {userLoader}}) => {
const user = await userLoader.load(node);
return user;
},
cursor: ({ cursor }) => cursor,
},