GraphQL nested query returning null - node.js

When attempting to run the following query I get this error: "Cannot return null for non-nullable field User.first_name.". I am not expecting a null result.
Query:
{
site(site_reference: 123456789) {
site_name
site_managers {
_id
first_name
}
}
}
What I expect to see is:
{
"data": {
"site": {
"site_name": "Test Site",
"site_managers": [
{
"_id": "5bbcd55ff7bd643be4d490fa",
"first_name": "Claire"
},
{
"_id": "5b9fa2e1f66fb32164f4d547",
"first_name": "John"
}
]
}
}
}
My MongoDB stores the _id in an array which it is able to return, however anything else from my user type returns null.
User Schema:
type User {
_id: String!,
first_name: String!,
last_name: String!,
role_id: Int!
email: String!,
password: String,
created_date: String!,
updated_date: String!,
last_login_date: String,
reset_password_token: String
}
type Query {
user(_id: String!): User!
users(role_id: Int): [User!]!
}
User Resolver:
Query: {
user: (parent, args) => User.findById(args._id),
users: (parent, args) => {
if (args.role_id) {
return User.find({ role_id: args.role_id })
}
return User.find({})
},
},
Site Schema:
type Site {
site_name: String!,
use_policy: String!,
redirect_url: String!,
intro_text: String!,
logo: String!,
address_line_1: String!,
address_line_2: String,
address_city: String!,
address_county: String!,
address_postcode: String!,
phone: String!,
site_reference: Int!,
site_managers: [User],
business_id: String!,
connect_duration: Int!,
created_date: String!,
updated_date: String!,
}
type Query {
site(site_reference: Int!): Site!
sites(business_id: Int!): [Site!]!
}
Site Resolver:
Query: {
site: (parent, args) =>
Site.findOne({ site_reference: args.site_reference }),
sites: (parent, args) => Site.find({ business_id: args.business_id }),
},
I imagine I need to do more in my resolver for the site query but I am unsure of what exactly. I have already tried using mongoose.populate but to no avail, the furthest I got was returning an empty array with the populate method.
Thanks in advance for your time.
Edit:
Here's my mongoose schemas
User Schema
const UserSchema = new mongoose.Schema({
first_name: { type: String, required: true },
last_name: { type: String, required: true },
role_id: { type: Number, required: true },
email: { type: String, required: true },
password: { type: String },
created_date: { type: Date, default: Date.now, required: true },
updated_date: { type: Date, default: Date.now, required: true },
last_login_date: { type: Date },
reset_password_token: { type: String },
})
Site Schema:
const SiteSchema = new mongoose.Schema({
site_name: { type: String, required: true },
use_policy: {
type: String,
required: true,
default: config.defaultUsePolicy,
},
redirect_url: {
type: String,
required: true,
default: config.defaultRedirect,
},
intro_text: {
type: String,
required: true,
default: config.defaultIntroText,
},
logo: { type: String, required: true, default: config.defaultLogo },
address_line_1: { type: String, required: true },
address_line_2: String,
address_city: { type: String, required: true },
address_county: { type: String, required: true },
address_postcode: { type: String, required: true },
phone: { type: String, required: true },
site_reference: { type: String, required: true },
site_managers: {type:[mongoose.Schema.Types.ObjectId], required: true}
business_id: { type: mongoose.Schema.Types.ObjectId, required: true },
connect_duration: { type: Number, required: true, default: 0 },
created_date: { type: Date, default: Date.now, required: true },
updated_date: { type: Date, default: Date.now, required: true },
})

If you're using MongoDB and mongoose, you will want to use populate to join your collections. In order to do so, in your schema, the field you're populating needs to not only have a type, but also a ref property that tells mongoose which Model to use to populate the field, for example:
// Note the actual ref should be whatever name you passed to mongoose.model
site_managers: [{type:mongoose.Schema.Types.ObjectId, ref: 'User'}]
Now, use populate inside your resolver:
site: (parent, { site_reference }) => {
return Site.findOne({ site_reference }).populate('site_managers').exec()
}
See the docs for more detailed examples.

Related

find value in array mongoose+typescript

I have an schema created with mongoose+typescript like this :
import mongoose, { Schema, Types, Document } from 'mongoose'
import { mongoosePagination, Pagination } from 'mongoose-paginate-ts'
import { Department } from './departmentModel'
import { Designation } from './designationModel'
import { Roles } from './roleModel'
export interface IEmployee extends Document {
userName: string
email: string
firstName: string
lastName: string
designation: Types.ObjectId
department: [Types.ObjectId]
roles: [Types.ObjectId]
projects: string
status: string
reportingManager: [
{
managerId: string
userName: string
},
]
approver: [
{
approverId: string
userName: string
},
]
createdBy: string
updatedBy: string
}
type employeeUser = mongoose.Document & {
userName: string
email: string
firstName: string
lastName: string
designation: Types.ObjectId
department: [Types.ObjectId]
roles: [Types.ObjectId]
projects: string
status: string
reportingManager: [
{
managerId: string
userName: string
},
]
approver: [
{
approverId: string
userName: string
},
]
createdBy: string
updatedBy: string
}
const schema = new Schema(
{
userName: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
designation: {
type: Schema.Types.ObjectId,
ref: Designation,
required: true,
},
department: [{
type: Schema.Types.ObjectId,
ref: Department,
required: true,
}],
roles: [
{
type: Schema.Types.ObjectId,
ref: Roles,
required: true,
},
],
status: {
type: Boolean,
default: true,
required: true,
},
reportingManager: [
{
managerId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
},
],
approver: [
{
approverId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
},
],
createdBy: {
type: String,
immutable: true,
},
updatedBy: {
type: String,
},
},
{ collection: 'user', timestamps: true },
)
schema.plugin(mongoosePagination)
const User: Pagination<IEmployee> = mongoose.model<employeeUser, Pagination<IEmployee>>('User', schema)
export { User }
And trying using like this::
import { IEmployee, User } from '#/models/user'
const getEmployeeDetailsHelper = async (paginationQuery: unknown): Promise<unknown> => {
try {
return await User.find({department: '623aca76efa7cf01ec7c39ac'}).exec()
} catch (err) {
logger.error(JSON.stringify(err))
return err
}
}
but typescript giving me error like:
No overload matches this call.
Overload 1 of 3, '(callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Argument of type '{ department: string; }' is not assignable to parameter of type 'Callback<IEmployee[]>'.
Object literal may only specify known properties, and 'department' does not exist in type 'Callback<IEmployee[]>'.
Overload 2 of 3, '(filter: FilterQuery<IEmployee>, callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Type 'string' is not assignable to type 'Condition<[ObjectId]>'.
Overload 3 of 3, '(filter: FilterQuery<IEmployee>, projection?: any, options?: QueryOptions, callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Type 'string' is not assignable to type 'Condition<[ObjectId]>'.ts(2769)
user.ts(13, 3): The expected type comes from property 'department' which is declared here on type 'FilterQuery<IEmployee>'
user.ts(13, 3): The expected type comes from property 'department' which is declared here on type 'FilterQuery<IEmployee>'
Please help.

MongoD: How to update specific field of a document in a collection?

I have a collection of jobs in MongoDB and in this collection there is a field in the documents named as appliedUser. I want to update this field when a new user applies for this job.So basically this field stores the id's of all the users who are applying for this job.
I am using findOneAndUpdate() function but not able to do it.
Job.findOneAndUpdate({ _id: req.params.id }, { $set: { appliedUser:
req.user.id } }, function(err, job) {
console.log(job);
})
and here is my Schema:
const jobSchema = new Schema({
date: { type: Date, default: Date() },
expirydate: { type: Date, required: true },
name: { type: String, required: true },
companydetails: { type: String, required: true },
address: { type: String, required: true },
role: { type: String, required: true },
city: { type: String, required: true },
minsalary: { type: Number, required: true },
maxsalary: { type: Number, required: true },
skills: { type: Array, required: true },
minex: { type: Number, required: true },
appliedUser: [{ type: Schema.Types.ObjectId, ref: 'users', unique:
true }],
user: { type: String, required: true }
})
The array of the document is not updating. I am not able to find the errors.
Look like what you need is $addToSet. Example:
Job.findOneAndUpdate({ _id: req.params.id }, { $addToSet: { appliedUser: req.user.id } }, function(err, job) {
console.log(job);
})

How create collection remotely form mlab api in remote-mongodb using nodejs,expressjs?

I am following Brad Traversy's MERN stack development course and In 4th section, I can't make post request to route localhost:5000/api/profile,
after sending post request with data as handle,status,skills which are fields in my collection, it returns error skills field required.
skills is array of strings sent from user-input.
when I checked collection-profile is created or not then it's not created and only one collection is shown as main collection user,
I followed each line of code in tutorial but getting error, I wanted to get Profile collection to be created at remote mlab mongodb,
my profile model code is
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
handle: {
type: String,
required: true,
max: 40
},
company: {
type: String
},
website: {
type: String
},
location: {
type: String
},
status: {
type: String,
required: true
},
skills: {
type: [String],
required: true
},
bio: {
type: String
},
githubusername: {
type: String
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
},
from: {
type: Date,
required: true
},
to: {
type: Date
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
fieldofstudy: {
type: String,
required: true
},
from: {
type: Date,
required: true
},
to: {
type: Date
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
instagram: {
type: String
}
},
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model("profile", ProfileSchema);
using validator.js module, I am validating input-fields,
if (Validator.isEmpty(data.skills)) {
errors.skills = "Skills field is required";
}
so, I can't find why exactly I can't find new collection as profile?
I tried using mongodb-locally, but it didn't help.
my Github repo link.
I returned user's details when route /api/users/current and changed response on success
before changing:
res.json({ msg: "Success" });
after:
passport.authenticate("jwt", { session: false }),
(req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
email: req.user.email
});
}
);
Here is my commit history,
then It didn't give error as skills field is required,

Mongoose Populate Virtuals

How to populate array of objects not by _id? I need it because I use "market_hash_name" as id in my project and it's more effectively then use _id:
let schema = new mongoose.Schema({
steamid: {
type: String,
unique: true,
index: true,
required: true
},
inventory: [{
market_hash_name: String,
name: String,
assetid: {
type: String
},
instanceid: {
type: String,
default: '0'
},
contextid: {
type: String,
default: '2'
},
amount: {
type: Number,
default: 1
},
ongoing_price_manipulation: {
type: Boolean
},
price: {
type: Number
}
}]
});
I used virtuals but only for a separate model and it works:
schema.virtual('item_data', {
ref: 'Steamlytics',
localField: 'market_hash_name',
foreignField: 'market_hash_name'
});
this.find(query).populate("item_data").exec((err, items) => {});

Mongoose - Cast to ObjectId failed for value "[object Object]" at path _id

I have the below schema:
var StorySchema = new Schema({
title: { type: String, required: true },
users: {
id: { type: Schema.ObjectId, ref: 'users' },
creator: { type: Boolean }
},
maxlines: { type: Number, default: '10'},
lines: {
text: { type: String },
entered_at: { type: Date },
user: {
id: { type: Schema.ObjectId, ref: 'users' }
}
},
created_date: { type: Date, default: Date.now },
updated_date: { type: Date, default: Date.now },
})
I've got the below which does the query:
exports.view = function (req, res) {
Stories
.findOne({
_id: req.params.id
})
/*.populate('users') */ **<-- If I uncomment this I get the error**
.exec(function (err, story) {
if (err) {
res.json(200, {
success: "false",
message: err.message
})
} else if (story) {
res.json({
sucess: "true",
message: story
})
} else {
res.json(200, {
sucess: "false",
message: "story not found"
})
}
})
}
As above if I add .populate('users') it flags the below error:
{
"success": "false",
"message": "Cast to ObjectId failed for value \"[object Object]\" at path \"_id\""
}
I'm calling /view/51fc2e02576f2dc058000001 (which is an Object ID of the stories table), without the .populate('users') if I call the URL it brings back the document.
The users -> id value is populated with ObjectId("51fbe87ec137760025000001") - which is a valid _id in the users collection
I cannot see what I'm missing?
Added User Schema
var UserSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
username: { type: String, required: true, unique: true },
provider: { type: String, required: true, enum: ['local', 'facebook'] },
password: { type: String, required: true },
avatar: { type: String, default: 'http://i.imgur.com/1PtcFos.jpg' },
gender: { type: String, required: true, uppercase: true, enum: ['M', 'F'] },
facebook: {
id: { type: String },
token: { type: String },
token_expiry: { type: Date }
},
device: {
token: { type: String },
type: { type: String, enum: ['ios', 'android'] },
badge: { type: Number },
id: { type: String },
created_date: { type: Date, default: Date.now },
updated_date: { type: Date, default: Date.now }
},
created_date: { type: Date, default: Date.now },
updated_date: { type: Date, default: Date.now }
})
I think you can only do .populate('users.id'). Populate is to use the reference Object to replace the id field. Please take a look at the doc.

Resources