I'm trying to make a mutation request to my graphql api but I'm getting an invalid value error. I'm using graphql codegen with typescript and react on my front end. I directly copied my working graphql mutation that I created on graphql playground. Codegen isn't giving any errors when I generate, typescript isn't giving me any errors and I have checked my syntax like 10 times but I can't for the life of me figure out why it's going wrong.
error code: GRAPHQL_VALIDATION_FAILED
error message: "Argument "data" has invalid value {email: $email, username: $username, password: $password, confirmPassword: $confirmPassword}."
My backend resolver looks like this
#Mutation(() => LoginResponse)
async signupLocal(
#Arg("data") signupLocalInput: SignupLocalInput,
#Ctx() { res }: MyContext
): Promise<LoginResponse> {
const user = await signupLocalService(signupLocalInput);
sendRefreshToken(res, createRefreshToken(user));
return {
accessToken: createAccessToken(user),
user,
};
}
My frontend graphql mutation looks like this
mutation SignupLocal(
$email: String!
$username: String!
$password: String!
$confirmPassword: String!
) {
signupLocal(
data: {
email: $email
username: $username
password: $password
confirmPassword: $confirmPassword
}
) {
accessToken
}
}
And my implementation looks like this
const [signupLocal] = useSignupLocalMutation();
const onSubmit = async (data: FormData) => {
console.log(data);
const { email, username, password, confirmPassword } = data;
const response = await signupLocal({
variables: { email, username, password, confirmPassword },
});
console.log(response);
};
Related
I am using TypeScript in my React application and I have an error "Property 'accessToken' does not exist on type 'User'" when getting an accessToken of user
My handler function in component:
const handleRegister = (email: string, password: string) => {
const auth = getAuth();
createUserWithEmailAndPassword(auth, email, password)
.then(({user}) => {
dispatch(setUser({
email: user.email,
token: user.accessToken,
id: user.uid
}));
});
}
after searching for a solution to this problem, I did this
token: (user as unknown as OAuthCredential).accessToken,
and IDE automatically added the following imports
import firebase from "firebase/compat";
import OAuthCredential = firebase.auth.OAuthCredential;
But after that I get an error in the console "Uncaught ReferenceError: firebase is not defined".
I feel stupid but I don't understand how to solve it
do this see what results you get, i think you need result.user.accessToken but im not sure
signInWithEmailAndPassword(auth, email, password)
.then((result: any) => {
// Signed in
console.log(result)
// ...
})
I have not tested this, but you can use something like below.
const handleRegister = (email: string, password: string) => {
const auth = getAuth();
createUserWithEmailAndPassword(auth, email, password)
.then(({user}) => {
user.getIdToken().then(function(idToken) {
// Send token to your backend via HTTPS
dispatch(setUser({
email: user.email,
token: idToken,
id: user.uid
}));
// ...
}).catch(function(error) {
// Handle error
});
});
}
I have defined the following mutation and inputtype:
extend type Mutation {
signup(input: SignupReq!): SignupStatus!
}
input SignupReq {
email: String!
password: String!
}
using graphql playground:
mutation signup{signup(input:{password:"blabla", email: "my#email.dk"}){success, message}}
it returns:
{
"data": {
"signup": {
"success": true,
"message": "Success!"
}
}
}
which is what I expect.
but how do I call this mutation from my React client?
what I have now is:
const SIGNUP_MUTATION = gql`
mutation SignupUser($input: SignupReq!) {
signup(signupReq: $input) {success, message, token}
}
`
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [signup, { data, loading, error }] = useMutation(SIGNUP_MUTATION)
const { data, loading, error } = await signup({ variables: { email, password } })
which is pretty much standard.
but I'm missing something I just cant pinpoint. Cause running the React script gives me the following error message:
'Field "signup" argument "input" of type "SignupReq!" is required, but it was not provided.',
How do I make the correct call to Graphql from react. I have been unable to find any documentation on how to use input types for mutations from React.
any help welcome.
Kim
You are passing in email and password as variables into your mutation:
variables: { email, password }
Instead, as you can see from your mutation, you have to pass in an input variable:
variables: {
input: {
email,
password
}
}
This will work, as it then mirrors the type used in your GraphQL schema:
input SignupReq {
email: String!
password: String!
}
What I am trying to do: using useMutation to submit an array of array to mongoDB.
What happened: it did successfully save in mongoDb and in the correct format BUT the 'result.data' of useMutation is not as expected, it's too weired to be used.
This is my array of array (of course, it started as an empty array and was pushed in as user adds attraction to the days in their itinerary).
let itinerary = [{placeIds: ["ChIJafBhcoVPqEcRb7rHEy3G0L8", "ChIJBRk3gUlOqEcR9oEBV-dqK5M"]},
{placeIds: ["ChIJx8Iw5VFOqEcRXUgfWxkmAaA", "ChIJSdWeck5OqEcReSTr3YfoSuE"]},
{placeIds: ["ChIJ1WCXcFJOqEcRBR_ICa3TemU"]}]
This is the graphql mutation:
const SUBMIT_ITINERARY = gql`
mutation submitItinerary(
$dayPlans: [DayPlanInput]
){
submitItinerary(
dayPlans: $dayPlans
){
id
dayPlans{
placeIds
}
createdAt
}
}
`
This is the apollo react hook useMutation:
const [submitItinerary] = useMutation(SUBMIT_ITINERARY, {
update(result){
console.log(result.data);
},
onError(err){
console.log(err)
},
variables: {
dayPlans: itinerary
}
})
in my graphql playground I got this
So I thought in my frontend I would get something similar but instead I got this:
Why is that the case? I want to get a structure similar to what I got in graphql playground.
More information that might be helpful:
Here is my itinerary schema:
const itinerarySchema = new Schema({
city: String,
username: String,
createdAt: String,
dayPlans: [],
user: {
type: Schema.Types.ObjectId,
ref: 'users',
}
});
my type definitions:
type DayPlan {
placeIds: [String]!
}
type Itinerary {
id: ID!
dayPlans: [DayPlan]!
username: String!
createdAt: String!
}
input DayPlanInput {
placeIds: [String]
}
input RegisterInput {
username: String!
password: String!
confirmPassword: String!
email: String!
}
type Query {
getUsers: [User]
}
type Mutation {
register(registerInput: RegisterInput): User!
login(username: String!, password: String!): User!
submitItinerary(dayPlans: [DayPlanInput] ): Itinerary!
}
and my submitItinerary resolver:
Mutation: {
async submitItinerary(_, {dayPlans}, context) {
const user = checkAuth(context);
//console.log(user);
if (dayPlans.length === 0){
throw new Error('Itinerary should not be empty');
}
const newItinerary = new Itinerary({
dayPlans,
user: user.id,
username: user.username,
createdAt: new Date().toISOString()
})
const submitted = await newItinerary.save()
return submitted;
}
}
The first variable of the update option in useMutation is the Apollo Cache itself not the mutation result. The mutation result can be found in the second parameter. Here you can find the API docs.
This should log the correct data:
const [submitItinerary] = useMutation(SUBMIT_ITINERARY, {
update(cache, result){
console.log(result.data);
},
onError(err){
console.log(err)
},
variables: {
dayPlans: itinerary
}
})
Also another way to access the mutation result is using the second element from the hook return value:
const [submitItinerary, { data, loading, error }] = useMutation(SUBMIT_ITINERARY)
I’m trying to do a mutation and I keep getting an error about data being null on the React side. However, if I try the same mutation in the GraphQL console it works. Also, I know the endpoint is working because I can Query data with no problem.
Everything
Server code (resolver):
async signup(parent, args, ctx, info) {
// lowercase their email
args.email = args.email.toLowerCase();
// hash their password
const password = await bcrypt.hash(args.password, 10);
// create the user in the database
const user = await ctx.db.mutation.createUser({
data: {
...args,
password,
}
}, info);
return user;
}
Mutation (React Side)
mutation signupUser($email: String!, $password: String!, $name: String!) {
signup(email: $email, password: $password, name: $name) {
__typename
id
email
password
name
}
}
TypeError: Cannot read property 'data' of undefined
at Mutation._this.onMutationCompleted (react-apollo.esm.js:477)
at react-apollo.esm.js:434
Also here is a snippet of my Mutation on the component
<Mutation
mutation={signUpUserMutation}
onCompleted={(user) => {
handleClose();
}}
onError={(error) => {
console.log(error)
setOpen(true);
}}
>
{signup => (
<Form
onSubmit={async (values, { setSubmitting }) => {
await signup({
variables: {
name: values.name,
email: values.email,
password: values.password,
},
});
setSubmitting(false);
}}
>
{({
values, errors, handleChange, handleBlur, isSubmitting,
}) => (
In your schema
type User {
id: Int
name: String
email: String
password: String
}
type Response {
status: Boolean
message: String
data: User
}
type Mutation {
signUp(name: String!, email: String!, password: String!) : Response
}
On Mutation resolver signUp function
async signUp(parent, args, { db }, info) {
// lowercase their email
args.email = args.email.toLowerCase();
// hash their password
const password = await bcrypt.hash(args.password, 10);
// create the user in the database
const data = {
...args,
password,
}
const user = await db.mutation.createUser(data, info);
return user;
}
in database mutation createUser function you can access like that
const createUser = async ({ name, email, password }, info) => {
try {
// Code to save user information in database
return { status: true,
message: "User registration successful. You can login now.",
data: createdUser
}
} catch(e) {
return { status: false, message: e.message, data: null }
}
}
And your query
mutation signupUser($email: String!, $password: String!, $name: String!) {
signup(email: $email, password: $password, name: $name) {
status
message
data {
id
email
password
name
}
}
}
I used the email-password template to setup my graphcool server with user authentication. I want to add a property for “name” when signing up a new user. I added name to the User model and updated the signup.graphql and signup.ts code as shown below.
I get an error when running the createUser mutation which says “name is not defined”. I’m not sure what the problem is. Any help is greatly appreciated!
signup.graphql
extend type Mutation {
signupUser(email: String!, password: String!, name: String): SignupUserPayload
}
signup.ts
interface EventData {
email: string
password: string
name: string
}
const SALT_ROUNDS = 10
export default async (event: FunctionEvent<EventData>) => {
console.log(event)
try {
const graphcool = fromEvent(event)
const api = graphcool.api('simple/v1')
const { email, password } = event.data
if (!validator.isEmail(email)) {
return { error: 'Not a valid email' }
}
// check if user exists already
const userExists: boolean = await getUser(api, email)
.then(r => r.User !== null)
if (userExists) {
return { error: 'Email already in use' }
}
// create password hash
const salt = bcrypt.genSaltSync(SALT_ROUNDS)
const hash = await bcrypt.hash(password, salt)
// create new user
const userId = await createGraphcoolUser(api, email, hash, name)
// generate node token for new User node
const token = await graphcool.generateNodeToken(userId, 'User')
return { data: { id: userId, token } }
} catch (e) {
console.log(e)
return { error: 'An unexpected error occured during signup.' }
}
}
async function getUser(api: GraphQLClient, email: string): Promise<{ User }> {
const query = `
query getUser($email: String!) {
User(email: $email) {
id
}
}
`
const variables = {
email,
}
return api.request<{ User }>(query, variables)
}
async function createGraphcoolUser(api: GraphQLClient, email: string, password: string, name: string): Promise<string> {
const mutation = `
mutation createGraphcoolUser($email: String!, $password: String!, $name: String) {
createUser(
email: $email,
password: $password,
name: $name
) {
id
}
}
`
const variables = {
email,
password: password,
name: name
}
return api.request<{ createUser: User }>(mutation, variables)
.then(r => r.createUser.id)
Found the answer. I was missing name from this line here..
const { email, password } = event.data
Changing to this solved it
const { email, password, name } = event.data