Strapi graphql mutation - graphql

I'm using strapi as a headless CMS
I've created a Customer content type and I've installed the graphql plugin.
I'm trying to create a mutation in the graphql playground to create a new Customer
mutation createCustomer($username: String!, $email: String, $password: String){
createCustomer(username: $username, email: $email, password: $password){
_id username email password
}
}
I'm passing the username etc in the query variables
{ "username": "user1" }
{ "email": "User1#test.com" }
{"password":"123456"}
I'm getting the error
"message": "Unknown argument \"username\" on field \"createCustomer\" of type \"Mutation\".",
If I look in the Docs the it looks like.
How can I write a mutation to create a new Customer

That error indicates that are missing the reference to Customer type in the Query component of the Mutation.
Also, I believe you are missing "input:" & "data:". I know that these are required when using createUser.
By example:
mutation createCustomer($username: String!, $email: String, $password: String) {
createCustomer(
input: { data: { username: $username, email: $email, password: $password } }
) {
customer {
id
username
email
password
}
}
}

Related

Cant Return Array in Graphql Query

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

How to document errors in graphql?

type Query {
"""
ErrorCode: EMAIL_DUPLICATED
type EmailDuplicatedError {
email: String!
source: UserSource!
}
enum UserSource {
Google
Facebook
Github
}
"""
register(email: String!, password: String!): AccessToken!
}
"""
The AccessToken scalar type is a string of 16 characters.
"""
scalar AccessToken
Hope you can get what I mean through the above schema. I'd like to know if there is any code generator can support errors documented this way, so I can reduce the code I write on both client and server side.
I don't want to define errors like the following
type Query {
register(email: String!, password: String!): RegisterResponse
}
type RegisterResponse {
accessToken: AccessToken
error: EmailDuplicatedError
}
type EmailDuplicatedError {
email: String!
source: UserSource!
}
enum UserSource {
Google
Facebook
Github
}
"""
The AccessToken scalar type is a string of 16 characters.
"""
scalar AccessToken
Because I'd like errors to be responded in errors field, and api only shows what you can get when you succeeded.
Thank you for your time reading this post.
There's some ways to do error handling with GraphQL, I'll recommend you two:
Using response extensions:
GraphQL JSON response.error has a field called extensions, you can use this to set a code field like:
{
"error": {
"errors": [
{
"message": "example",
"extensions": {
"code": "YOUR CODE"
}
}
]
}
}
Using unions:
There is an medium post by Sasha Solomon that talks about that:
https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc
Using the examples in this post, the way to handle graphql errors with unions is like this:
type User {
id: ID!
name: String
}
type Suspended {
reason: String
}
type IsBlocked {
message: String
blockedByUser: User
}
type UnavailableInCountry {
countryCode: Int
message: String
}
"User, or reason we can't get one normally"
union UserResult = User | IsBlocked | Suspended
type Query {
user: UserResult!
}

Apollo / GraphQL / Prisma "Login" Mutation Won't Return All User Fields

I'm relatively new to the world of GraphQL / Prisma / Apollo and working on an app where I need login functionality, and access restrictions based upon a User's permissions.
When I run my login mutation it fails if I request the User's "roles" field to be returned and I can't for the life of me figure out why.
If I do a simple query on all Users and request the "roles" field it returns no problem.
Likewise if I do a query on a single User and filter by any other field: - id, email, username.
My app is setup as this:
datamodel.graphql (prisma)
type User {
id: ID! #id #unique
name: String!
email: String! #unique
username: String! #unique
password: String!
roles: [UserRoles!]!
}
enum UserRoles {
ADMIN
USER
DIRECTOR
}
schema.graphql (react / apollo)
type User {
id: ID!
name: String!
email: String!
username: String!
password: String!
roles: [UserRoles!]!
}
enum UserRoles {
ADMIN
USER
DIRECTOR
}
Mutation.js (resolvers front end)
async loginUser(parent, args, { prisma }, info) {
const { username, password } = args.data;
const user = await prisma.query.user({ where: { username } });
if (!user) {
throw new Error("Invalid login credentials. Please try again");
}
const passwordIsMatch = await bcrypt.compare(password, user.password);
if (!passwordIsMatch) {
throw new Error("Invalid login credentials. Please try again");
}
return {
user,
token: generateToken(user.id),
};
},
If I run the login mutation as something like this it runs fine.
mutation LoginUser($username: String!, $password: String!){
loginUser(data: {
username: $username
password: $password
}) {
user {
id
name
}
token
}
}
But if I run it like this it fails with a message of "Cannot return null for non-nullable field User.roles."
mutation LoginUser($username: String!, $password: String!){
loginUser(data: {
username: $username
password: $password
}) {
user {
id
name
roles
}
token
}
}
Any ideas on how to fix this? Or should I wait for the response on this mutation and then run a query for a user with the id which is returned and then pull the "roles" field from that? That seems a little superfluous to me, as I thought one of the great benefits of GraphQL was minimising the number of http requests.
The query fails because the roles field was declared as required in your schema, but your resolver obviously returns no roles for the user.
To pass the validation, you need to change the field spec to optional in both GraphQL and Prisma schemas (remove the !s):
roles: [UserRoles]

Cannot query field 'password' on type 'User' graphql

I'm using graphql and prisma.
datamodel.prisma
type User {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
email: String! #unique
password: String!
first_name: String
}
schema.graphql
scalar Date
type Query {
users: [User!]!
}
type User {
id: ID!
createdAt: Date!
updatedAt: Date!
email: String!
first_name: String
}
resolver
users: (parent, args, context) => {
return context.prisma.users();
}
I expected to get a user list, but received the error:
query
{
users {
email
}
}
error
"Cannot query field 'password' on type 'User'. (line 7, column 5):\n password\n ^"
UPDATE 1
Tried to use a fragment, but got the same:
{
users {
...userFields
}
}
fragment userFields on User {
email
}
I'd like to also add a scenario that can very easily cause this same issue that took me a while to debug and I'm sure others will encounter, because it took me quite some time to realize the issue was actually being caused in my FRONTEND code where I was defining my auth-related Mutations.
Set Up
Here's what that looked like while developing much of my application:
datamodel.prisma (I've omitted some fields for simplicity sake)
type User {
id: ID! #id
name: String!
email: String! #unique
password: String!
}
schema.graphql (just showing the signUp Mutation for simplicity)
type Mutation {
signUp(email: String!, password: String!, name: String!): User!
}
SignUp.js (where I access the signUp Mutation exposed in schema.graphql)
const SIGNUP_MUTATION = gql`
mutation SIGNUP_MUTATION(
$email: String!
$name: String!
$password: String!
) {
signUp(email: $email, name: $name, password: $password) {
id
email
name
password
}
}
`
Notice that I am returning id, email, name, and password - this was because I wanted to make sure everything was working in development.
Introducing the Cannot query field 'password' on type 'User' error
Once I began working on security and created a special User type in schema.graphql so that I could hide protected fields such as password, that's when I got this issue:
schema.graphql (notice that I am now not exposing the password field on this frontend-facing User type)
type Mutation {
signUp(email: String!, password: String!, name: String!): User!
}
type User {
id: ID!
name: String!
email: String!
}
Solution
Because of the nature of this error message, I spent most of my morning puzzling over my backend code. But it turned out that the error was actually being caused in SignUp.js, where I was RETURNING the password field.
The solution was to simply remove that line from the list of return fields like so:
const SIGNUP_MUTATION = gql`
mutation SIGNUP_MUTATION(
$email: String!
$name: String!
$password: String!
) {
signUp(email: $email, name: $name, password: $password) {
id
email
name
}
}
`
Key Lessons
So if you're experiencing this issue, please check ALL of your relevant mutations and make sure that you're not returning any fields that you have protected as I described here.
Be sure to also check your frontend code and make sure you aren't trying to return fields that you have now protected and are no longer exposing to the frontend.
I hope this is helpful and saves people some time!
... aaah Prisma ...
I don't know if interfaces, unions or input types are supported. Graphql docs
Prisma generates almost everything ... but defining password as required (as type for DBB generation) for datamodel should not block querying for a type subset or type defined on existing model without using all fields.
For me it's a bit missleading error message. It can be resolver related.
Try to match types in resolver, don't return direct prisma query (operates on model types), but map queried data (an array) to filter out password field/property (to be query type compatible). It's a security concern, too - passwords shouldn't be read from outside.
I've created custom query which return a fragment and seems the error gone.
Just run in your console(in prisma folder):
PRISMA_MANAGEMENT_API_SECRET=mysecret42 prisma deploy

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