简体   繁体   English

GraphQl 突变:addUser 未创建用户

[英]GraphQl Mutation: addUser not creating user

I'm refactoring a Google Books app from a Restful API to GraphQL, and I am stuck on a mutation not behaving the way I expect.我正在将 Google Books 应用程序从 Restful API 重构为 GraphQL,但我卡在了一个突变中,它的行为与我预期的不同。

When a user fills out the form found on Signup.js the Mutation ADD_USER should create a user within Mongoose, this user should have a JWT token assigned to them, and user should be logged in upon successful execution of the Mutation.当用户填写 Signup.js 上的表单时,Mutation ADD_USER 应在 Mongoose 内创建一个用户,该用户应分配给他们一个 JWT 令牌,并且用户应在成功执行 Mutation 后登录。

Actions observed: • Mutation is being fired off from the front end.观察到的行为: • 突变正在从前端发射。 When I open developer tools in the browser I can see the Username, Email and Password being passed as variables.当我在浏览器中打开开发人员工具时,我可以看到用户名、Email 和密码作为变量传递。 • I have tried console logging the token, and keep getting an undefined return • When I try to run the mutation in the GraphQL sandbox I get a null value returned. • 我已尝试在控制台记录令牌,并不断收到未定义的返回值 • 当我尝试在 GraphQL 沙箱中运行突变时,我收到了 null 返回值。 • When I console log the args in resolvers.js no value appears on the console, which tells me the request is not reaching the resolver. • 当我在控制台记录resolvers.js 中的args 时,控制台上没有显示任何值,这告诉我请求没有到达解析器。

SignupForm.js (React FE Page) SignupForm.js (React 前端页面)

import React, { useState } from "react";
import { Form, Button, Alert } from "react-bootstrap";
import { useMutation } from "@apollo/client";
import { ADD_USER } from "../utils/mutations";
import Auth from "../utils/auth";

const SignupForm = () => {
  // set initial form state
  const [userFormData, setUserFormData] = useState({
    username: "",
    email: "",
    password: "",
  });
  // set state for form validation
  const [validated] = useState(false);
  // set state for alert
  const [showAlert, setShowAlert] = useState(false);

  const [addUser] = useMutation(ADD_USER);

  const handleInputChange = (event) => {
    const { name, value } = event.target;
    setUserFormData({ ...userFormData, [name]: value });
  };

  const handleFormSubmit = async (event) => {
    event.preventDefault();

    // check if form has everything (as per react-bootstrap docs)
    const form = event.currentTarget;
    if (form.checkValidity() === false) {
      event.preventDefault();
      event.stopPropagation();
    }

    try {
      ///Add user is not returning data. payload is being passed as an object

      const response = await addUser({
        variables: { ...userFormData },
      });

      if (!response.ok) {
        throw new Error("OH NO!SOMETHING WENT WRONG!");
      }

      const { token, user } = await response.json();
      console.log(user);
      Auth.login(token);
    } catch (err) {
      console.error(err);
      setShowAlert(true);
    }

    setUserFormData({
      username: "",
      email: "",
      password: "",
    });
  };

Mutation.js突变.js

export const ADD_USER = gql`
  mutation addUser($username: String!, $email: String!, $password: String!) {
    addUser(username: $username, email: $email, password: $password) {
      token
      user {
        username
        email
      }
    }
  }
`;

typeDefs.js typeDefs.js

const { gql } = require("apollo-server-express");

const typeDefs = gql`
  input SavedBooks {
    authors: [String]
    description: String
    bookId: String
    image: String
    link: String
    title: String
  }
  type Books {
    authors: [String]
    description: String
    bookId: ID
    image: String
    link: String
    title: String
  }
  type User {
    _id: ID
    username: String
    email: String
    password: String
    savedBooks: [Books]
  }
  type Auth {
    token: ID!
    user: User
  }
  type Query {
    me: User
  }
  type Mutation {
    ##creates a user profile through the Auth type, that way we can pass a token upon creation
    addUser(username: String!, email: String!, password: String!): Auth

    login(email: String!, password: String!): Auth

    saveBook(bookData: SavedBooks): User

    deleteBook(bookId: ID!): User
  }
`;
module.exports = typeDefs;

resolvers.js解析器.js

const { User, Book } = require("../models");
const { AuthenticationError } = require("apollo-server-express");
const { signToken } = require("../utils/auth");


const resolvers = {
  Query: {
    me: async (parent, args, context) => {
      if (context.user) {
        return User.findOne({ _id: context.user._id }).populate("books");
      }

      throw new AuthenticationError("You need to log in");
    },
  },
};
Mutation: {
  //try refactoring as a .then
  addUser: async (parent, args) => {
    //create user profile
    await console.log("resolver test");
    console.log(args);
    const user = await User.create({ username, email, password });
    //assign token to user
    const token = signToken(user);
    return { token, user };
  };

  login: async (parent, { email, password }) => {
    const user = User.findOne({ email });
    if (!user) {
      throw new AuthenticationError("Invalid Login Credentials");
    }
    const correctPw = await profile.isCorrectPassword(password);
    if (!correctPw) {
      throw new AuthenticationError("Invalid Login Credentials");
    }
    const token = signToken(user);
    return { token, user };
  };

  saveBook: async (parent, { bookData }, context) => {
    if (context.user) {
      return User.findOneAndUpdate(
        { _id: context.user._id },
        { $addToSet: { savedBooks: bookData } },
        { new: true }
      );
    }
    throw new AuthenticationError("You need to log in");
  };

  deleteBook: async (parent, { bookId }, context) => {
    if (context.user) {
      return User.findOneAndUpdate(
        { _id: contex.user._id },
        //remove selected books from the savedBooks Array
        { $pull: { savedBooks: context.bookId } },
        { new: true }
      );
    }
    throw new AuthenticationError("You need to log in");
  };
}
module.exports = resolvers;

auth.js auth.js

const jwt = require("jsonwebtoken");

// set token secret and expiration date
const secret = "mysecretsshhhhh";
const expiration = "2h";

module.exports = {
  // function for our authenticated routes
  authMiddleware: function ({ req }) {
    // allows token to be sent via  req.query or headers
    let token = req.query.token || req.headers.authorization || req.body.token;

    // ["Bearer", "<tokenvalue>"]
    if (req.headers.authorization) {
      token = token.split(" ").pop().trim();
    }

    if (!token) {
      return req;
    }

    // verify token and get user data out of it
    try {
      const { data } = jwt.verify(token, secret, { maxAge: expiration });
      req.user = data;
    } catch {
      console.log("Invalid token");
      return res.status(400).json({ message: "invalid token!" });
    }

    // send to next endpoint
    return req;
  },
  signToken: function ({ username, email, _id }) {
    const payload = { username, email, _id };

    return jwt.sign({ data: payload }, secret, { expiresIn: expiration });
  },
};

Basically, I have combed from front to back end looking for where I introduced this bug, and am stuck.基本上,我从前端到后端进行了梳理,寻找我在哪里引入了这个错误,然后卡住了。 Any suggestions or feedback is greatly appreciated.非常感谢任何建议或反馈。

I was able to figure out the issue.我能够弄清楚这个问题。 First, a syntax error on resolver.js was preventing my mutations from being read.首先,resolver.js 的一个语法错误阻止了我的突变被读取。

Next, I made the following adjustment to handleFormSubmit on SignupForm.js接下来,我对 SignupForm.js 上的 handleFormSubmit 做了如下调整

  try {
      ///Add user is not returning data. payload is being passed as an object

      const {data} = await addUser({
        variables: { ...userFormData },
      });
     console.log(data)
      console.log(userFormData)

  
      **Auth.login(data.addUser.token);**
    } catch (err) {
      console.error(err);
      setShowAlert(true);
    }

That way my FE was properly accounting for what my Auth Middleware was passing back after successful user creation.这样我的 FE 就可以正确地说明我的 Auth 中间件在成功创建用户后传回的内容。 Thanks for your help xadm, being able to talk this out got me thinking about where else to attack the bug.感谢 xadm 的帮助,能够讨论这个问题让我开始思考还有什么地方可以攻击这个 bug。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM