I'm trying to learn graphql. I didn't find any good course witch will help me to learn it. So i started building it with little examples. In this moment i'm getting error when i'm trying to open http://127.0.0.1:3000/graphql. it's telling me "message": "Must provide query string."
I thinks i did something wrong with my users query?
This is my complete code of it. Can someone please help...
// user type
const UserType = new GraphQLObjectType({
name: "User",
description: "User Type",
fields: () => ({
id: { type: GraphQLInt },
firstname: { type: GraphQLString },
lastname: { type: GraphQLString },
email: { type: GraphQLString },
password: { type: GraphQLString },
}),
});
// register (mutation)
const register = {
type: UserType,
args: {
firstname: { type: GraphQLString },
lastname: { type: GraphQLString },
email: { type: GraphQLString },
password: { type: GraphQLString },
},
//#ts-ignore
async resolve(parent, args) {
const { firstname, lastname, email, password } = args;
const user = new User();
user.firstname = firstname;
user.lastname = lastname;
user.email = email;
user.password = password;
const result = await user.save();
console.log(result);
return result;
},
};
// users (query)
const users = {
// type: new GraphQLList(UserType),
type: UserType,
args: { id: { type: GraphQLInt } },
//#ts-ignore
async resolve(parent, args) {
const users = await User.find();
return users;
},
};
const MutationType = new GraphQLObjectType({
name: "MutationType",
description: "Mutations",
fields: { register },
});
const QueryType = new GraphQLObjectType({
name: "QueryType",
description: "Queries",
fields: { users },
});
const schema = new GraphQLSchema({ query: QueryType, mutation: MutationType });
app.use(
"/graphql",
graphqlHTTP({
schema,
graphiql: true,
})
);
I have 2 problems.
when i type in browser: http://127.0.0.1:3000/graphql it's don't loading. it's telling me Loading... and stuck on it.
when i try it in insomnia it's telling me
{
"errors": [
{
"message": "Must provide query string."
}
]
}
how can i fixed it ?
Related
Using Express-graphql, mongo/mongoose/ react.
Im Creating a database with Teams, Players, and Matches.
I want to write a mutation for creating a new team, which lists its players given their IDs, but I keep getting errors. I'm a newbie to GraphQL, so explain it accordingly please.
Can you help me populate "players" please?
The Models
const TeamSchema = new mongoose.Schema({
teamName: { type: String },
teamNumber: { type: Number },
inMatchIDs: { type: mongoose.Schema.Types.ObjectId, ref: "Match" },
players: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
});
const UserSchema = new mongoose.Schema(
{
name: { type: String },
wins: { type: Number },
matchesPlayed: { type: Number },
},
{ timestamps: true }
);
The GraphQL Schema
const TeamType = new GraphQLObjectType({
name: "Team",
fields: () => ({
id: { type: GraphQLID },
teamName: { type: GraphQLString },
teamNumber: { type: GraphQLInt },
inMatchIDs: { type: new GraphQLList(MatchType) },
players: {
type: UserType,
resolve(parent, args) {
return parent.players.map((player) => {
User.findById(player.id);
});
},
},
}),
});
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
wins: { type: GraphQLInt },
matchesPlayed: { type: GraphQLInt },
email: { type: GraphQLString },
password: { type: GraphQLString },
token: { type: GraphQLString },
}),
});
The Mutation Schema
createTeam: {
type: TeamType,
args: {
teamName: { type: GraphQLString },
players: { type: GraphQLList(GraphQLID) },
},
resolve(parent, args) {
const team = new Team({
teamName: args.teamName,
players: args.players,
});
return team.save();
},
},
... and the Request / Response
//send request:
mutation {
createTeam(teamName: "champions", players: ["63382ba421b2cbfcd0531f4c", "63382ba421b2cbfcd0531f4c"]) {
teamName
id
teamNumber
players{
name
}
}
}
//Response:
{
"data": {
"createTeam": {
"teamName": "champions",
"id": "63386d0a850a34f9823fd4cd",
"teamNumber": null,
"players": {
"name": null
}
}
}
}
Mongo:
So after a whole day and a half trying to wrap my head around the relationships, here is how I solved it:
The Mongoose model remains unchanged:
const TeamSchema = new mongoose.Schema({
teamName: { type: String },
teamNumber: { type: Number },
inMatchIDs: { type: mongoose.Schema.Types.ObjectId, ref: "Match" },
players: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
});
The TeamType in the Schema, is how the response displays the information and the subfields (see 'players' here) need to be resolved as this:
The TeamType
const TeamType = new GraphQLObjectType({
name: "Team",
fields: () => ({
id: { type: GraphQLID },
teamName: { type: GraphQLString },
teamNumber: { type: GraphQLInt },
inMatchIDs: { type: new GraphQLList(MatchType) },
players: {
type: GraphQLList(UserType),
resolve(parent, args) {
let players = [];
parent.players.map((id, i) => {
players.push(User.findById(id));
});
return players;
},
},
}),
});
But another important syntax to be careful of is the passing of the information in the args of the mutation:
The Mutation
createTeam: {
type: TeamType,
args: {
teamName: { type: GraphQLString },
players: { type: GraphQLList(GraphQLID) }, //no need for [] as GraphQLList spells it out
},
players: { type: GraphQLNonNull(GraphQLList(GraphQLID)) }, // here I am defining that the value for the key 'players' will be a non-nullable list of GraphQLID type
resolve(parent, args) {
const team = new Team({
teamName: args.teamName,
players: args.players, // so that here, the model for "Team" understand that I am passing it Ids
});
return team.save();
},
},
There are two objects, Users and Company, each user works for a Company, thereby has a companyId which is referred to through GraphQL. Similarly, each Company has a list of users working for them.
Here's the code:
company.js
const UserType = require('./user')
const CompanyType = new GraphQLObjectType({
name: 'Company',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
users: {
type: new GraphQLList(UserType), --------> Error:Expected {} to be a GraphQL type.
Expected UserType to be a GraphQL type.
async resolve(parentValue, args) {
return await axios(
`http://localhost:3001/company/${parentValue.id}/users`
).then(({ data }) => data)
},
},
}),
})
user.js
const CompanyType = require('./company')
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: GraphQLString },
age: { type: GraphQLInt },
name: { type: GraphQLString },
company: {
type: CompanyType,
async resolve(parentValue, args) {
console.log(parentValue)
return await axios(
`http://www.localhost:3001/company/${parentValue.companyId}`
).then((response) => response.data)
},
},
}),
})
rootquery.js
const UserType = require('./user')
const CompanyType = require('./company')
const RootQuery = new GraphQLObjectType({
name: 'RootQueryObjectType',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQLString },
},
resolve: async (parentValue, args) => {
return await axios(`http://localhost:3001/users/${args.id}`).then(
({ data }) => data
)
},
},
company: {
type: CompanyType,
args: {
id: { type: GraphQLString },
},
resolve: async (parentValue, args) => {
return await axios(`http://localhost:3001/company/${args.id}`).then(
({ data }) => data
)
},
},
},
})
The error is understandable due to Circular Dependencies.
In case I put the code of user.js and company.js into the rootquery.js, there's no error.
Is there a way to seperate out these files, without running into an empty object error?
After researching quite a bit, found out that wherever you'd need to use a circular dependency, you've got to require it at that point in your code, instead of requiring it as a variable.
E.g.
Previously:
const UserType = require('./user') ---> This was null/empty as it wasn't yet
created
const CompanyType = new GraphQLObjectType({
name: 'Company',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
users: {
type: new GraphQLList(UserType), --------> Error:Expected {} to be a
GraphQL type.
Expected UserType to be a
GraphQL type.
async resolve(parentValue, args) {
return await axios(
`http://localhost:3001/company/${parentValue.id}/users`
).then(({ data }) => data)
},
},
}),
})
To overcome this issue, I required the neccessary module exactly where it is required
Solution:
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
name: { type: GraphQLString },
id: { type: GraphQLString },
age: { type: GraphQLInt },
company: {
**type: require('./company'),** -----> This is where a dynamic
import
is being made
async resolve(parentValue, args) {
return await axios(
`http://localhost:3001/users/${parentValue.id}`
).then(({ data }) => data)
},
},
}),
})
I have user model
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
const userSchema = mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
isEmailVerified: { type: Boolean },
registrationStep: { type: Number, enum: [0,1,2,3]},
regDate: { type: Date },
companyName: { type: String },
oib: { type: String },
telephone: { type: String },
address: { type: String },
city: { type: String },
countryCode: { type: String },
postalCode: { type: String },
userType: { type: String, enum:['firms','drivers','both']},
approved: { type: Boolean },
isAdmin: { type: Boolean }
});
userSchema.plugin(uniqueValidator);
module.exports = mongoose.model("User", userSchema);
and documents model
const mongoose = require("mongoose");
const docsSchema = mongoose.Schema({
docsPath: { type: String, required: true },
creator: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
docType: { type: String, enum:['isr','lmp','pocmr'], required: true }
});
module.exports = mongoose.model("Docs", docsSchema);
I want to join collections so that for every user exists the user_docs field with docs data as in code below. I'm trying to do that with joining _id with creator as it is defined when creating a document
exports.getUsersAndDocs = (req, res, next) => {
const query = User.aggregate([
{
$lookup: {
from: "Docs",
localField: "_id",
foreignField: "creator",
as: "user_docs"
}
}]);
query
.then(fetchedUsers => {
res.status(200).json({
message: "Users fetched successfully!",
users: fetchedUsers,
});
})
.catch(error => {
res.status(500).json({
message: "Fetching users failed!"
});
});
};
I'm recieveing empty user_docs array. Thank you for your help
If someone has the same problem. I solved it by changing
from: "Docs"
to
from: Docs.collection.name
i am having issue to user JWT with sequelize, cuz i need to create a model method and then apply this model method whenever i want to perform a validation.
the problem is that when I try to use "this" the return is "user".
can someone give me an explanation about why and some possible workaround? ty.
const { DataTypes } = require('sequelize')
const sequelize = require('../sequelize/db')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const User = sequelize.define('user', {
id: {
type: DataTypes.STRING,
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.STRING,
require: true,
allowNull: false,
unique: true,
validate: {
len: [4, 12]
}
},
password: {
type: DataTypes.STRING,
require: true,
allowNull: false,
validate: {
len: {
args: [6, 12],
msg: 'password between 6 and 12 characters'
},
async set(password) {
this.setDataValue('password', await bcrypt.hash(password, 8))
}
}
},
email: {
type: DataTypes.STRING,
require: true,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
avatar: {
type: DataTypes.BLOB,
},
profile: {
type: DataTypes.TEXT
}
})
User.genAuthToken = async function () {
const user = this
const token = jwt.sign({ _id: user.id }, 'qweasd', { expiresIn: '7 days' })
return token
}
const a = async () => {
await User.create({
id: 'asdd',
name: 'nishia',
email: 'emaia#am.me',
password: 'asdasd'
})
const token = await User.genAuthToken()
}
a()
You need an instance method not a class (static) method:
User.prototype.genAuthToken = async function () {
const user = this
const token = jwt.sign({ _id: user.id }, 'qweasd', { expiresIn: '7 days' })
return token
}
...
const a = async () => {
const newUser = await User.create({
id: 'asdd',
name: 'nishia',
email: 'emaia#am.me',
password: 'asdasd'
})
const token = await newUser.genAuthToken()
}
When I run the following Query:
{
viewer {
id
firstName
lastName
}
}
I get the following object which displays the incorrect query results:
{
"data": {
"viewer": {
"id": "VXNlcjo=",
"firstName": null,
"lastName": null
}
}
}
I have set up the following database.js:
import Sequelize from 'sequelize';
import bcrypt from 'bcrypt';
// Create a Sequelize instance
var connection = new Sequelize('dev', 'mary', 'password', {
host: 'localhost',
dialect: 'postgres',
pool: {
max: 5,
min: 0,
idle: 10000
}
});
// User model
var User = connection.define('user', {
// First name
firstName: {
type: Sequelize.STRING,
allowNull: false,
validate: {
is: {
args: /^[a-z]+$/i,
msg: 'First name is invalid'
}
}
},
// Last name
lastName: {
type: Sequelize.STRING,
allowNull: false,
validate: {
is: {
args: /^[a-z]+$/i,
msg: 'Last name is invalid'
}
}
},
// Username
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
is: {
args: /^#?(\w){3,15}$/,
msg: 'Username can not exceed 15 characters or contain spaces.'
}
}
},
// Email
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
// Password
password: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
// Validate Password
}
}
}, {
// Use plural names in database.
freezeTableName: false,
// Timestamps
timestamps: true,
// hooks
hooks: {
afterValidate: user => {
user.password = bcrypt.hashSync(user.password, 8);
}
}
});
// Group model
var Group = connection.define('group', {
// Owner Id
ownerId: {
type: Sequelize.INTEGER,
allowNull: false
},
// Group name
name: {
type: Sequelize.STRING,
allowNull: false
},
// Domain
domain: {
type: Sequelize.STRING,
allowNull: false
},
// Topic
topic: {
type: Sequelize.STRING,
validate: {
len: {
args: [0, 150],
msg: 'Topic can not exceed 150 characters.'
}
}
},
// Access
private: {
type: Sequelize.BOOLEAN
},
// Password
password: {
type: Sequelize.STRING,
defaultValue: null,
validate: {
// Validate Password
}
}
}, {
freezeTableName: false,
// Timestamps
timestamps: true,
// hooks
hooks: {
afterValidate: user => {
if (user.password) {
user.password = bcrypt.hashSync(user.password, 8);
}
}
}
});
// Relationships
User.belongsToMany(Group, { through: 'UserGroup' });
Group.belongsToMany(User, { through: 'UserGroup' });
// Insert some data by calling sync
connection.sync({
force: true
}).then ( () => {
// First user dummy
User.create( {
firstName: 'Sam',
lastName: 'Smith',
username: 'samsmith',
email: 'samsith#some.com',
password: 'somepassword'
})
.then(user => {
user.createGroup({
ownerId: user.id,
name: 'Engineering',
topic: 'A group',
domain: user.email.split('#')[1],
private: true,
password: ''
});
});
// Second user dummy
User.create( {
firstName: 'Sam',
lastName: 'Joshua',
username: 'samjoshua',
email: 'samjoshua#gmail.com',
password: 'somepassword'
})
.then(user => {
user.createGroup({
ownerId: user.id,
name: 'Design',
topic: 'This is some group',
domain: user.email.split('#')[1],
private: false,
password: ''
});
});
})
.catch(error => {
console.log(error);
});
export default connection;
and the following schema.js file:
import {
GraphQLBoolean,
GraphQLFloat,
GraphQLID,
GraphQLInt,
GraphQLList,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
} from 'graphql';
import {
connectionArgs,
connectionDefinitions,
connectionFromArray,
fromGlobalId,
globalIdField,
mutationWithClientMutationId,
nodeDefinitions,
} from 'graphql-relay';
import db from './database';
/**
* We get the node interface and field from the Relay library.
*
* The first method defines the way we resolve an ID to its object.
* The second defines the way we resolve an object to its GraphQL type.
*/
var {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
var {type, id} = fromGlobalId(globalId);
if (type === 'User') {
return db.models.user.getUser(id);
} else if (type === 'Group') {
return db.models.group.getGroup(id);
} else {
return null;
}
},
(obj) => {
if (obj instanceof User) {
return userType;
} else if (obj instanceof Group) {
return groupType;
} else {
return null;
}
}
);
/**
* We define our own types here.
*/
var userType = new GraphQLObjectType({
name: 'User',
description: 'A person who users our app',
fields: () => ({
id: globalIdField('User'),
firstName: {
type: GraphQLString,
description: 'A users first name',
resolve(user) {
return user.firstName;
}
},
lastName: {
type: GraphQLString,
resolve(user) {
return user.lastName;
}
},
username: {
type: GraphQLString,
resolve(user) {
return user.username;
}
},
email: {
type: GraphQLString,
resolve(user) {
return user.email;
}
},
groups: {
type: new GraphQLList(groupType),
resolve(user) {
return user.getGroups();
}
},
}),
interfaces: [nodeInterface],
});
// Group query
var groupType = new GraphQLObjectType({
name: 'Group',
description: 'A users group',
fields: () => ({
id: globalIdField('Group'),
ownerId: {
type: GraphQLInt,
resolve(group) {
return group.ownerId;
}
},
name: {
type: GraphQLString,
resolve(group) {
return group.name;
}
},
topic: {
type: GraphQLString,
resolve(group) {
return group.topic;
}
},
domain: {
type: GraphQLString,
resolve(group) {
return group.domain;
}
},
private: {
type: GraphQLBoolean,
resolve(group) {
return group.private;
}
},
users: {
type: new GraphQLList(userType),
resolve(group) {
return group.getUsers();
}
}
}),
interfaces: [nodeInterface],
});
/**
* This is the type that will be the root of our query,
* and the entry point into our schema.
*/
var queryType = new GraphQLObjectType({
name: 'Query',
description: 'This is the root query',
fields: () => ({
node: nodeField,
// Add our own root fields here
viewer: {
type: userType,
args: {
id: {
type: GraphQLInt
},
email: {
type: GraphQLString
}
},
resolve: (root, args) => {
return db.models.user.findAll({ where: args });
}
},
}),
});
/**
* This is the type that will be the root of our mutations,
* and the entry point into performing writes in our schema.
*/
var mutationType = new GraphQLObjectType({
name: 'Mutation',
description: 'Data insertion',
fields: () => ({
addUser: {
type: userType,
args: {
firstName: {
type: new GraphQLNonNull(GraphQLString),
},
lastName: {
type: new GraphQLNonNull(GraphQLString),
},
username: {
type: new GraphQLNonNull(GraphQLString),
},
email: {
type: new GraphQLNonNull(GraphQLString),
},
password: {
type: new GraphQLNonNull(GraphQLString),
}
},
resolve(_, args) {
return Db.models.user.create({
firstName: args.firstName,
lastName: args.lastName,
username: args.username,
email: args.email.toLowerCase(),
password: args.password
});
}
}
})
});
/**
* Finally, we construct our schema (whose starting query type is the query
* type we defined above) and export it.
*/
export var Schema = new GraphQLSchema({
query: queryType,
mutation: mutationType
});
I can't seem to find where I have made a mistake. The database file seems fine since I can see the data in the dev table so I guess my error is somewhere in the schema.js file. Thank you for the help.