Cant Return Array in Graphql Query - graphql

I'm pretty new to graphql and I'm working on a project in nodejs where I am trying to return users when a getUsers query is performed. The issue is that when I test this query in graphql studio, I'm getting an error stating: "GraphQLError: Cannot query field \"users\" on type \"User\". I'm really confused as to why I'm having this issue. I've seen a number of examples where people where able to return just an array and didn't have a problem, but every time I've tried this I end up getting a similar error. Due to this, I've only been able to return a value for a query or mutation when I am super specific such as for my user query:
...
const user = await requireAuth(user)
return {
_id: user._id,
username: user.username,
firstName: user.firstName,
email: user.email,
}
Does anyone know why this is happening? I would really appreciate any help or advice. Thank you!
Query getUsers in graphql,
{
getUsers {
users
}
}
Query in user-resolvers.js
getUsers: async(parent, args, context, info) => {
try {
let users = await User.find()
console.log(users)
// console.log(users) shows all of the users in the format found in type Users
return users;
}
catch (error) {
throw error;
}
},
schema.js
export default`
type Users {
_id: ID!
username: String
email: String!
firstName: String
lastName: String
basicInfo: [BasicInfo]!
avatar: String
date: Date
}
type BasicInfo {
birth_date: String!
age: Int!
feet: Int!
inches: Int!
}
...
type Query {
getUsers: [Users]
}
...
schema {
query: Query
mutation: Mutation
}
`;
index.js
import UserResolvers from './user-resolvers.js';
import User from '../../models/User.js';
export default {
Query: {
user: UserResolvers.user,
getUsers: UserResolvers.getUsers,
},
...
};

In the query you specify the fields you want to return and you don't have a field users, you must only specify fields that exist in your schema:
{
getUsers {
id
username
email
...
}
}
More info here

Related

GraphQL mutation takes an array as an input parameter and returns a json String

I'm trying to implement mutation query with an array as a parameter and String as a return type.
Here is my schema file:
input OrganizationInput {
orgId: String!
orgName: String!
}
type Mutations {
importOrganizations(input: [OrganizationInput]): String
}
Here is my mutation:
mutation importOrganizations($orgs: [OrganizationInput]) {
importOrganizations(
input: {
orgId: id,
orgName: name
}
)
}
This code doesn't work, but I don't know how to do it properly.
Maybe someone more experienced in GraphQL could help me?
Do you have any errors that can help?
Anyways your mutation need to return fields, e.g.:
mutation importOrganizations($orgs: [OrganizationInput]) {
importOrganizations(
input: {
orgId: id,
orgName: name
})
{
id
name
}
}

Apollo and Prisma Graphql explicit model relationship not queryable

I have begun testing out prisma 2 and graphql in general for a new application. I am running into an issue with an explicit many to many table on being able to query relations.
Here is my apollo schema:
scalar DateTime
type Query {
user(id: String!): User
users: [User]
spaces: [Space]
roles: [Role]
}
type Mutation {
createUser(id: String!, email: String!): User!
createSpace(name: String!): Space!
}
type User {
id: ID!
email: String!
spaces: [UserSpace!]
createdAt: DateTime!
updatedAt: DateTime!
}
type Space {
id: ID!
name: String!
users: [UserSpace!]
createdAt: DateTime!
updatedAt: DateTime!
}
type Role {
id: ID!
name: String!
description: String!
users: UserSpace
createdAt: DateTime!
updatedAt: DateTime!
}
type UserSpace {
id: ID!
user: User!
space: Space!
role: Role!
createdAt: DateTime!
updatedAt: DateTime!
}
Here is my prisma schema:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// npx prisma migrate dev
// npx prisma generate
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String #id
email String #unique
spaces UserSpace[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model Space {
id Int #default(autoincrement()) #id
name String #unique
users UserSpace[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model Role {
id Int #default(autoincrement()) #id
name String #unique
description String
users UserSpace[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model UserSpace {
id Int #default(autoincrement()) #id
user User #relation(fields: [userId], references: [id])
userId String
space Space #relation(fields: [spaceId], references: [id])
spaceId Int
role Role #relation(fields: [roleId], references: [id])
roleId Int
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
Here is my mutations resolver:
const { prisma } = require(".prisma/client");
async function createUser(parent, args, context, info) {
return await context.prisma.user.create({
data: {
...args,
},
});
}
async function createSpace(parent, args, context, info) {
const isAuthenticated = context.authentication.isAuthenticated;
let role = null;
if(!isAuthenticated) {
throw new Error("Not Authenticated");
}
try {
role = await context.prisma.role.findUnique({
where: {
name: "Space Administrator",
},
});
}
catch(err) {
throw new Error(err);
}
return await context.prisma.space.create({
data: {
...args,
users: {
create: {
role: {
connect: { id: role.id },
},
user: {
connect: { id: context.authentication.auth0Id },
},
},
},
},
});
}
module.exports = {
createUser,
createSpace,
}
Here is my user resolver (I know this is where the problem is however I do not know how to solve the issue):
function spaces(parent, args, context, info) {
return context.prisma.user.findUnique({ where: { id: parent.id } }).spaces();
}
module.exports = {
spaces,
}
Basically when I create the space the user is added as a Space Administrator to the space and then should be able to be queried with the following:
query {
users {
id
email
spaces {
id
role {
name
}
space {
name
}
}
createdAt
}
}
However when I run the query I get the following error:
"message": "Cannot return null for non-nullable field UserSpace.role.",
How in prisma 2 do I make the resolver for the users work with an explicit many to many table and how it has the third relation in there? I am new to prisma and graphql so if there anything else that stands out also I would like to have the input.
I'm using the word type to refer to object-models in your GraphQL schema and model to refer to data-models in your Prisma Schema.
The Problem
I see that you have a User type resolver, that has a resolver function for User.spaces field in your User type. The query that you have defined in your User.spaces resolver will return the relevant userSpace records from the database.
However, these userSpace records do not by default resolve the role field, as it is a relation field. This is how prisma works (relation fields are not resolved by default, unless explicitly stated).
Solution
Create a resolver for the UserSpace type and explicitly define the the resolver function for UserSpace.role field. This is what it will look like
// UserSpace resolver module
function role(parent, args, context, info) {
return context.prisma.userSpace.findUnique({ where: { id: parent.id } }).role();
}
module.exports = {
role,
}
While there are some other ways to solve this problem, the way I have shown (along with the specific syntax) is recommended because under the hood it allows prisma to perform certain optimizations to solve the n+1 query problem. But, if you don't know what that is, you don't necessarily need to worry about it either.
Did you provide the value to users arg? like this: users(id: "string_value"). Because is id is required.

Enumerating all fields from a GraphQL query

Given a GraphQL schema and resolvers for Apollo Server, and a GraphQL query, is there a way to create a collection of all requested fields (in an Object or a Map) in the resolver function?
For a simple query, it's easy to recreate this collection from the info argument of the resolver.
Given a schema:
type User {
id: Int!
username: String!
roles: [Role!]!
}
type Role {
id: Int!
name: String!
description: String
}
schema {
query: Query
}
type Query {
getUser(id: Int!): User!
}
and a resolver:
Query: {
getUser: (root, args, context, info) => {
console.log(infoParser(info))
return db.Users.findOne({ id: args.id })
}
}
with a simple recursive infoParser function like this:
function infoParser (info) {
const fields = {}
info.fieldNodes.forEach(node => {
parseSelectionSet(node.selectionSet.selections, fields)
})
return fields
}
function parseSelectionSet (selections, fields) {
selections.forEach(selection => {
const name = selection.name.value
fields[name] = selection.selectionSet
? parseSelectionSet(selection.selectionSet.selections, {})
: true
})
return fields
}
The following query results in this log:
{
getUser(id: 1) {
id
username
roles {
name
}
}
}
=> { id: true, username: true, roles: { name: true } }
Things get pretty ugly pretty soon, for example when you use fragments in the query:
fragment UserInfo on User {
id
username
roles {
name
}
}
{
getUser(id: 1) {
...UserInfo
username
roles {
description
}
}
}
GraphQL engine correctly ignores duplicates, (deeply) merges etc. queried fields on execution, but it is not reflected in the info argument. When you add unions and inline fragments it just gets hairier.
Is there a way to construct a collection of all fields requested in a query, taking in account advanced querying capabilities of GraphQL?
Info about the info argument can be found on the Apollo docs site and in the graphql-js Github repo.
I know it has been a while but in case anyone ends up here, there is an npm package called graphql-list-fields by Jake Pusareti that does this. It handles fragments and skip and include directives.
you can also check the code here.

Any reason I am getting back the query name in the GraphQL results?

Using the makeExecutableSchema with the following Query definition:
# Interface for simple presence in front-end.
type AccountType {
email: Email!
firstName: String!
lastName: String!
}
# The Root Query
type Query {
# Get's the account per ID or with an authToken.
getAccount(
email: Email
) : AccountType!
}
schema {
query: Query
}
And the following resolver:
export default {
Query: {
async getAccount(_, {email}, { authToken }) {
/**
* Authentication
*/
//const user = security.requireAuth(authToken)
/**
* Resolution
*/
const account = await accounts.find({email})
if (account.length !== 1) {
throw new GraphQLError('No account was found with the given email.', GraphQLError.codes.GRAPHQL_NOT_FOUND)
}
return account
}
}
}
When I query with:
query {
getAccount(email: "test#testing.com") {
firstName
lastName
}
}
I am getting the following result in GraphiQL:
{
"data": {
"getAccount": {
"firstName": "John",
"lastName": "Doe"
}
}
}
So, any reason I am getting this "getAccount" back in the result?
Because getAccount is not a query name. It's just a regular field on the root query type Query.
And having results on the exact same shape as the query is one of the core design principles of GraphQL:
Screenshot from http://graphql.org/ site
Query name in GraphQL goes after query keyword:
query myQueryName {
getAccount(email: "test#testing.com") {
firstName
lastName
}
}

Take result from one query / mutation and pipe to another

I'm wondering if there's a way in GraphiQL that will let me pipe the result from one query / mutation into another query / mutation. Here's an example where the login mutation is used to get a viewer and the viewer would be used to query the addresses for that user. Is this possible with GraphQL / GraphiQL.
mutation {
login(credentials: {
email: "me#me.com",
password: "password123",
passwordConfirmation: "password123"
}) {
viewer
}
}
query {
addresses(viewer:viewer) {
city
}
}
The purpose of the selection set in the mutations is to be able to fetch data that has changed as a result of the mutation. But it also makes it possible to fetch related data, as long as you can access is through the mutation result type.
Let's assume we have following types:
type Address {
city: String
}
type User {
addresses: [Address]
}
If the result (payload) type of the login mutation includes a field viewer of type User that refers to the successfully logged in user, you can query any field of the User in the result of the mutation:
mutation {
login(credentials: {
email: "me#me.com",
password: "password123",
passwordConfirmation: "password123"
}) {
viewer {
addresses {
city
}
}
}
}

Resources