not sure what I'm doing wrong?
I'm getting undefined when trying to upload my form data to my supabase the data is coming to the API undefined but when I pass it to the function it prints what I want to send to the API in my submit handler.
export const Quote = () => {
const [formIsValid, setFormIsValid] = useState(false);
//----------------------------_FORM VALIDATION------------------------------------
const {
value: firstName,
inputBlurChangeHandler: firstNameBlur,
isValid: firstNameValid,
hasError: firstNameInputHasError,
valueChangeHandler: firstNameChangeHandler,
reset: resetFirstName,
} = useInput((value) => value.trim() !== "");
**hooks & useEffect removed to shorten question they are same as above but different names**
console.log(formIsValid, "FORM IS VALID");
const formSubmitHandler = async (event) => {
event.preventDefault();
//UNDEFINEDS
await fetch("api/dbhandler", {
method: "POST",
body: {
firstname: firstName,
secondname: secondName,
street: streetAddress,
phone: phoneNumber,
email: emailAddress,
postal: postalCode,
about: quoteDescription,
},
headers: {
"Content-Type": `text/plain`,
},
});
};
API is coming as undefined in req. body but if I console log in the submit handler values are being passed to the function not sure what I am doing wrong
import { supabase } from "../../utils/supabaseClient";
const supabaseApiHandler = async (req, res) => {
console.log(req.body.firstname);
if (req.method === "POST") {
const firstname = req.body.firstname;
const secondname = req.body.secondname;
const email = req.body.email;
const street = req.body.street;
const postal = req.body.postal;
const phone = req.body.phone;
const about = req.body.about;
const { data, error } = await supabase.from("quotes").insert([
{
firstname,
secondname,
email,
street,
postal,
phone,
about,
},
]);
}
res.status(200).json({ name: "test" });
};
export default supabaseApiHandler;
If you have the body parser disabled in the API route, req.body will be empty.
I accidentally left this code in without using another body parser.
export const config = {
api: {
bodyParser: false,
},
};
Related
I have create backend using express and mongodb database. I am trying to fetch data in react but getting an error while fetching the data as show. Please can anyone tell what the solution of above error is and how can i fetch data from the backend
const Register = () => {
const [values, setValues] = useState({
name: "",
age: "",
country: "",
email: "",
});
const setData = (e) => {
console.log(e.target.value);
const { name, value } = e.target;
setValues((val) => {
return {
...val,
[name]: value,
};
});
};
const addData = async (e) => {
e.preventDefault();
const { name, age, country, email } = values;
const res = await fetch("/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
age,
country,
email,
}),
});
const data = await res.json();
console.log(data);
if (res.status === 404 || !data) {
console.log("Error");
} else {
console.log("Data added successfully");
}
};
Here below is the backend code where the post function is performed.
router.post("/register", async (req, res) => {
const { name, age, country, email } = req.body;
if (!name || !age || !country || !email) {
res.status(404).send("Some data is missing");
}
try {
const preuser = await Crud.findOne({ email: email });
console.log(preuser);
if (preuser) {
res.status(404).send("The user already exists");
} else {
let addUser = new Crud({
name,
age,
country,
email,
});
addUser = await addUser.save();
res.status(201).json(addUser);
console.log(addUser);
}
} catch (error) {
res.status(404).send(error);
}
});
await fetch leads to an exception when the HTTP status is ≥ 400. You must add a try-catch block to handle such exceptions:
try {
const res = await fetch("/register", {...});
} catch(exception) {
// Handle the exception
}
Also, HTTP status 404 should be used when a resource is not found. You use it when a user already exists (where status 400 would be more appropriate) or in case of a database error (when 500 would be more appropriate).
i am trying to store user data in mongodb using put method .my intension is to update those data if there exist or create new. but my method create new data insteade of updating..
this code is from client side(here first i uploaded my image to the imgbb then i save it to server).. .
const image = data.image[0];
const formData = new FormData();
formData.append('image',image)
const API_KEY = '4957c3c668ded462db1fb1002c4535e6';
const url = `https://api.imgbb.com/1/upload?key=${API_KEY}`;
fetch(url,{
method : 'POST',
body : formData,
})
.then(res => res.json())
.then(result => {
if(result.success){
console.log('image',result.data.url)
const img = result.data.url;
const dataOfuser = {
user : user?.displayName,
email : user?.email,
phone: data.phone,
city: data.city,
education: data.education,
img: img
}
console.log(dataOfuser)
fetch(`http://localhost:5000/user/:${user.email}`,{
method:"PUT",
headers:{
'Content-Type': 'application/json'
},
body : JSON.stringify(dataOfuser)
})
.then(res => res.json())
.then(data => {
if(data.acknowledged){
toast.success('Profile Updated')
}
})
}
})
};
these are the code from mongodb
app.put('/user/:email',async(req,res)=>{
const email = req.params.email;
const user = req.body;
const filter = { email: email };
const options = { upsert: true };
const updateDoc = {
$set: user,
};
const result = await userCollection . updateOne ( filter , updateDoc , options);
res.send(result);
});
You could try to send email and user as body and
app.put("/user/email", async (req, res) => {
const { dataOfuser } = req.body; //this finds all
const { email, user, phone, city, etc} = dataOfuser; //this extracts the values, but probably better to send each values by themselves
You can also just send user and email etc on their own, instead of as one object:
body: JSON.stringify({ user, email, phone, city, education, img })
You can also try to add curlybraces around dataOfuser in the body: JSON.stringify({ dataOfuser })
const res = await userCollection
.updateOne(
{ email: email} *find the user by his email*
{ $set: {email: email, phone: phone, city: city etc.}}) *set email to email from body*
})
You should also have default value in inputs as their current value, so the fields doesn't get overwritten as null or blank
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.
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.
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.
• 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.
• 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.
SignupForm.js (React FE Page)
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
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
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
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
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.
Next, I made the following adjustment to handleFormSubmit on SignupForm.js
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. Thanks for your help xadm, being able to talk this out got me thinking about where else to attack the bug.
I follow modular or component based strucutre. I found a sample repo.
https://github.com/sujeet-agrahari/node-express-clean-architecture
So, there is a main component.module.js files which is responsible for connecting all other pieces like controller, route, and services.
For controller, services are being injected using higher order functions. Now, controller are super easy to test, I can stub or mock services easily.
auth.module.js
const router = require('express').Router();
const {
makeExpressCallback,
makeValidatorCallback,
} = require('../../middlewares');
// validator
const AuthValidator = require('./auth.validator');
// service
const { doRegister, doLogin, doCheckUserExist } = require('./auth.service');
const { BadRequestError } = require('../../utils/api-errors');
// controller
const controller = require('./auth.controller');
const register = controller.register({ BadRequestError, doCheckUserExist, doRegister });
const login = controller.login({ doCheckUserExist, doLogin });
const AuthController = { register, login };
// routes
const routes = require('./auth.routes')({
router,
AuthController,
AuthValidator,
makeValidatorCallback,
makeExpressCallback,
});
module.exports = {
AuthController,
AuthService: {
doCheckUserExist,
doLogin,
doRegister,
},
AuthRoutes: routes,
};
auth.controller.js
const login = (doCheckUserExist, doLogin) => async (httpRequest) => {
const { username, password } = httpRequest.body;
const userData = await doCheckUserExist({ username });
const loginData = {
username,
role: userData.role_id,
passedPassword: password,
actualPassword: userData.password,
};
const loginResult = await doLogin(loginData);
return {
statusCode: 200,
body: {
success: true,
message: 'Successfully logged in!',
data: loginResult,
},
};
};
const register = ({ BadRequestError, doCheckUserExist, doRegister }) => async (httpRequest) => {
const { username, password } = httpRequest.body;
try {
await doCheckUserExist({ username });
} catch (error) {
// user doesn't exist
const registerResult = await doRegister({ username, password });
return {
statusCode: 200,
body: {
success: true,
message: 'Registered successfully!',
data: registerResult,
},
};
}
throw new BadRequestError('User already exist!');
};
module.exports = { register, login };
Things are fine with the controller, now the problem is with the services. I can't find any pattern to make them thin and clean.
auth.services.js
const {
JWT_ACCESS_TOKEN_SECRET,
ACCESS_TOKEN_EXPIRES_IN,
SIGN_OPTION,
} = require('config');
const bcrypt = require('bcryptjs');
const { User } = require('../../db');
const { generateJWT } = require('./jwt.service');
const { NotFoundError, BadRequestError } = require('../../utils/api-errors');
const doRegister = async ({ username, password }) => {
const user = await User.create({
username,
password,
role_id: 1, // assign role id here
});
// generate access token
const payload = {
username,
role: user.role_id,
};
const token = await generateJWT({
secretKey: JWT_ACCESS_TOKEN_SECRET,
payload,
signOption: {
...SIGN_OPTION,
expiresIn: ACCESS_TOKEN_EXPIRES_IN,
},
});
return {
access_token: token,
...payload,
};
};
const doLogin = async ({
username, userRole, passedPassword, actualPassword,
}) => {
const isValidPass = bcrypt.compareSync(passedPassword, actualPassword);
if (!isValidPass) throw new BadRequestError('Username or Password is invalid!');
// generate access token
const payload = {
username,
role: userRole,
};
const token = await generateJWT({
secretKey: JWT_ACCESS_TOKEN_SECRET,
payload,
signOption: {
...SIGN_OPTION,
expiresIn: ACCESS_TOKEN_EXPIRES_IN,
},
});
return {
access_token: token,
...payload,
};
};
const doCheckUserExist = async ({ username }) => {
const user = await User.findOne({
where: {
username,
},
});
if (!user) throw new NotFoundError('User not found!');
return user;
};
module.exports = { doRegister, doLogin, doCheckUserExist };
A lot is happening in the services, model imports, constants imports, and other utils.
Now services become really hard to test.
Is there any way or pattern I can separate some logic from services and make them lighter?
I can implement reository pattern for db methods, but I am not aware how I can implement using sequelize?
Should I use also higher order function to inject all the utils and constants in the service like I did for controller?
I'm using firebase admin SDK on cloud functions to create users using
admin.auth().createUser({
email: someEmail,
password: somePassword,
})
now I want user to signIn using signInWithEmailAndPassword('someEmail', 'somePassword') but I cannot.
I get the following error
{code: "auth/user-not-found", message: "There is no user record corresponding to this identifier. The user may have been deleted."}
There doesn't seem to be a reason to Stringify/Parse. This worked after I struggled with an unrelated typo...
FUNCTION CALL FROM REACT JS BUTTON CLICK
<Button onClick={() => {
var data = {
"email": "name#example.com",
"emailVerified": true,
"phoneNumber": "+15551212",
"password": "randomPW",
"displayName": "User Name",
"disabled": false,
"sponsor": "Extra Payload #1 (optional)",
"study": "Extra Payload #2 (optional)"
};
var createUser = firebase.functions().httpsCallable('createUser');
createUser( data ).then(function (result) {
// Read result of the Cloud Function.
console.log(result.data)
});
}}>Create User</Button>
And in the index.js in your /functions subdirectory:
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
// CREATE NEW USER IN FIREBASE BY FUNCTION
exports.createUser = functions.https.onCall(async (data, context) => {
try {
const user = await admin.auth().createUser({
email: data.email,
emailVerified: true,
password: data.password,
displayName: data.displayName,
disabled: false,
});
return {
response: user
};
} catch (error) {
throw new functions.https.HttpsError('failed to create a user');
}
});
Screen shot of console output
In 2022 there still is no method built into the Admin SDK that would allow to create users in the emulator.
What you can do is to use the REST API of the emulator to create users there directly. The API is documented here: https://firebase.google.com/docs/reference/rest/auth#section-create-email-password
Provided you have got and nanoid installed you can use the following code to create users in the emulator.
import { nanoid } from 'nanoid'
import httpClientFor from '../lib/http-client/client.js'
const httpClient = httpClientFor('POST')
export const createTestUser = async ({ email = `test-${nanoid(5)}#example.io`, password = nanoid(10), displayName = 'Tony' } = {}) => {
const key = nanoid(31)
const { body: responseBody } = await httpClient(`http://localhost:9099/identitytoolkit.googleapis.com/v1/accounts:signUp?key=${key}`, {
json: {
email,
password,
displayName
}
})
const responseObject = JSON.parse(responseBody)
const { localId: userId, email: userEmail, idToken, refreshToken } = responseObject
return { userId, userEmail, idToken, refreshToken }
}
Please note: As there is no error handling implemented, this snippet is not suitable for production use.
Try like that
And please be ensure that user is created from the panel
admin.auth().createUser({
email: "user#example.com",
emailVerified: false,
phoneNumber: "+11234567890",
password: "secretPassword",
displayName: "John Doe",
photoURL: "http://www.example.com/12345678/photo.png",
disabled: false
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully created new user:", userRecord.uid);
})
.catch(function(error) {
console.log("Error creating new user:", error);
});
Just in case anyone else comes across this I was able to fix it with the help of this.
Here is a working example inside of an onCreate cloud function:
exports.newProjectLead = functions.firestore
.document('newProjectForms/{docId}')
.onCreate(async (snapshot) => {
const docId = snapshot.id
// this is what fixed it the issue
// stringify the data
const data = JSON.stringify(snapshot.data())
// then parse it back to JSON
const obj = JSON.parse(data)
console.log(obj)
const email = obj.contactEmail
console.log(email)
const password = 'ChangeMe123'
const response = await admin.auth().createUser({
email,
password
})
data
const uid = response.uid
const dbRef = admin.firestore().collection(`clients`)
await dbRef.doc(docId).set({
id: docId,
...data,
uid
}, {
merge: true
})
console.log('New Client Created')
})