I am new to redux, and working on an application. My sign-in and sign-up functionalities are working almost fine, except the fact that if some incorrect actions are being dispatched and not able to locate which part of the code is doing it. Below I am posting some code snippets.
Look at the second ghost LOGIN_FULFILLED Request, it should not occur as I don't have that user in the DB yet!
Screenshot for the actions and state transitions
Login action creators:
import request from 'axios';
import thunk from 'redux-thunk';
import store from '../store'
export function loginFunc(username, password) {
return {
type: 'LOGIN',
username,
password,
payload : request
.post("http://localhost:5000/users/authenticate",
{
username : username,
password: password
}
)
.then(function (response) {
console.log(response);
if (response.data.message === "user_found")
store.dispatch({type: 'LOGIN_FULFILLED', payload : response.data.results});
else
store.dispatch({type: 'LOGIN_REJECTED', payload : "user_not_found"});
})
.catch(function (error) {
console.log(error);
store.dispatch({type: 'LOGIN_REJECTED', payload : error});
})
}
}
Redux Thunk middleware allows you to write action creators that return a function instead of an action (as written in official guide).
You need to make few changes to make use of thunk. You need not import store to use getState and dispatch as they are arguments to the callback.
return function(dispatch, getState)
This dispatch, getState is same as store.dispatch and store.getState.
import request from 'axios';
export function loginFunc(username, password) {
return function(dispatch) {
request
.post("http://localhost:5000/users/authenticate", {
username: username,
password: password
})
.then(function(response) {
console.log(response);
if (response.data.message === "user_found")
dispatch({
type: 'LOGIN_FULFILLED',
payload: response.data.results
});
else
dispatch({
type: 'LOGIN_REJECTED',
payload: "user_not_found"
});
})
.catch(function(error) {
console.log(error);
dispatch({
type: 'LOGIN_REJECTED',
payload: error
});
})
}
}
Related
So im trying to build my register method without re-enventing nothing crazy with the create-t3-app stack with nextjs, trpc and nextauth:
export const signUpRouter = router({
signup: publicProcedure.input(UserModel).mutation(async ({ ctx, input }) => {
debugger;
try {
const { nickname, email, password } = input;
//check duplicate users
const checkingUsers = await ctx.prisma.user.findFirst({
where: {
email,
},
});
if (checkingUsers) {
return { message: "User already exists..." };
}
//hash password
return await ctx.prisma.user.create({
data: {
nickname,
email,
password: await hash(password, 12),
},
});
} catch (error: any) {
console.log("error", error);
throw new Error(error);
}
}),
});
export default signUpRouter;
This file is inside pages/api/auth/signup.ts
Should I have this on the server part ?
I have the router on my appRouter file
export const appRouter = router({
userLogin: userLoginRouter,
auth: authRouter,
signin: signInRouter,
signup: signUpRouter,
});
And when clicking on the register button:
async function onSumitRegisterValues(values: any) {
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
};
await fetch("http://localhost:3000/api/auth/signup", options)
.then((res) => res.json())
.then((data) => {
if (data?.ok) router.push("http://localhost:3000");
});
}
The values form contains nickname, email, password and cpassword to confirm password.
im getting a 500 on post
Server Error
TypeError: resolver is not a function
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Maybe its my lack of knowledge with trpc and next but ngl, its making me want to separate my backend into something different. But since im not rushing in building this project i really want to try to figure out what i shoud be doing better.
Why are you using fetch instead of using your useQuery method from trpc? The whole point of trpc is that you can skip fetch and will also have type safety.
https://trpc.io/docs/useQuery
I am using NextAuth to enable users to sign up/in with their Google account and also to link their Google account to their current account on my site.
In order to differentiate between signing up and linking an account when already signed in, I want to pass an extra parameter to signIn that I can access in the signIn callback that will allow me to take the correct action. I have tried:
signIn("google", null, { linkAccount: "true" });
However, this is only passed into the signIn request as a query parameter and is not passed through to the callback. Is there any way I can make a custom argument accessible in the callback?
Edit: Including more code below.
Call to next-auth's signIn client API:
signIn("google", null { linkAccount: "true" });
[...nextauth.js]
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import axios from 'axios';
const authOptions = (req) => ({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
secret: "secret",
callbacks: {
async signIn({
user, account, profile, email, credentials
}) {
// GOAL: How can I specify to this endpoint that I am just linking an account?
let res = await axios.post('http://localhost:8000/users/third_party_sign_in', {
third_party_id: user.id,
email: user.email,
type: account.provider
justLink: true|false
}, { withCredentials: true })
let path;
if (res.data.action === "login") {
path = `/?action=${res.data.action}&id=${res.data.user_id}&email=${user.email}&third_party=${account.provider}`
} else if (res.data.action === "create") {
path = `/?action=${res.data.action}&name=${user.name}&email=${user.email}&third_party=${account.provider}&third_party_id=${user.id}`
}
return path;
},
async redirect({ url }) {
return Promise.resolve(url)
}
},
});
function testNextApiRequest(req) {
if (req.query.nextauth
&& req.query.nextauth.length === 2
&& req.query.linkAccount) {
/// logs for signIn API call but not for callback
console.log("QUERY PARAMS: ", req.query);
}
}
export default (req, res) => {
testNextApiRequest(req);
return NextAuth(req, res, authOptions(req));
}
I also spent a lot of time on this trying to figure out how to get a param in a callback when using the signIn function.
Here's the solution
call signIn like you were doing
signIn("google", null, { linkAccount: "true" });
Now in [...nextauth].ts you want to parse req.query BEFORE passing it to next-auth like so
authOptions is just a function that returns next-auth callbacks and config.
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
console.log(req.query); // This will have your linkAccount param
return await NextAuth(req, res, authOptions(req, res));
}
Now that you have access to the params you can do whatever logic you want. This will only work for some callbacks like redirect. Trying to get the params in the session callback is still proving to be impossible for me.
It isn't great, honestly it's pretty bad if you do db queries you'll be slowing down all the requests but I think this is currently the best way to do it. Hope it helps!
More discussion here https://github.com/nextauthjs/next-auth/discussions/901
I have a next.js app that has several API routes that I am hoping to protect from users who are not logged in. Using next-auth, I understand that I can add the following code to each API route to achieve this.
import { getSession } from 'next-auth/client'
export default async (req, res) => {
const session = await getSession({ req })
if (session) {
res.send({ content: 'This is protected content. You can access this content because you are signed in.' })
} else {
res.send({ error: 'You must be sign in to view the protected content on this page.' })
}
}
However, I was wondering if it is possible to use API middlewares, so I am not repeating the same code over and over again? I read through the Next.js API middlewares documentation (https://nextjs.org/docs/api-routes/api-middlewares) and did the following:
import Cors from 'cors';
import { getSession } from 'next-auth/react';
function initMiddleware(middleware) {
return (req, res) =>
new Promise((resolve, reject) => {
middleware(req, res, async (result) => {
const session = await getSession({ req });
if (!session) {
return reject(result);
}
return resolve(result);
});
});
}
const cors = initMiddleware(
Cors({
methods: ['GET', 'POST', 'OPTIONS'],
})
);
export default async function handler(req, res) {
await cors(req, res);
\* fetching from database *\
Although it works, the following error is returned when I tried to access the API route when unauthenticated, and it feels like I'm not doing it properly.
error - null
wait - compiling /_error (client and server)...
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:561:11)
at DevServer.renderError (/Users/alextung/Desktop/Projects/askit/node_modules/next/dist/server/next-server.js:1677:17)
at DevServer.run (/Users/alextung/Desktop/Projects/askit/node_modules/next/dist/server/dev/next-dev-server.js:452:35)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async DevServer.handleRequest (/Users/alextung/Desktop/Projects/askit/node_modules/next/dist/server/next-server.js:325:20) {
code: 'ERR_HTTP_HEADERS_SENT'
}
error - Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Would really appreciate some help on this given that this is my first time working with middlewares. Thank you!
I am struggling with the async Redux (thunk). I trully don't understand what I am doing wrong with my async actions and why I get the error : Error: Actions must be plain objects. Use custom middleware for async actions.
export async function startLocalizationFetchingAsync(currentLocalizationState) {
return (dispatch) => {
let payload = {
request: {
sent:true,
}
};
dispatch({
type: "NEW_LOCALIZATION_REQUEST_SENT2",
payload: payload,
});
return axios.get("http://freegeoip.net/json/"+currentLocalizationState.clientIP)
.then(res => {
res = res.data;
var payload = {
country: res.country_name||'',
};
dispatch({
type: "NEW_LOCALIZATION",
payload: payload,
});
})
.catch(function (error) {
console.log("Promise Rejected",error);
dispatch({
type: "NEW_LOCALIZATION_REQUEST_ERROR",
payload: null,
});
});
};
}
while in the index.js router i have the following code
async action({ next, store }) {
// Execute each child route until one of them return the result
const route = await next();
await store.dispatch(startLocalizationFetchingAsync());
this generates me an error:
Error: Actions must be plain objects. Use custom middleware for async actions.
dispatch
webpack:///~/redux/es/createStore.js:153
http://myskyhub.ddns.net:3000/assets/client.js:9796:16
http://myskyhub.ddns.net:3000/assets/vendor.js:46309:16
Object.dispatch
webpack:///~/redux-thunk/lib/index.js:14
Object._callee$
webpack:///src/routes/index.js?a731:35
tryCatch
webpack:///~/regenerator-runtime/runtime.js:65
Generator.invoke
webpack:///~/regenerator-runtime/runtime.js:303
Generator.prototype.(anonymous
webpack:///~/regenerator-runtime/runtime.js:117
http://myskyhub.ddns.net:3000/assets/3.9645f2aeaa83c71f5539.hot-update.js:8:361
while the config store is the following
const middleware = [thunk.withExtraArgument(helpers), thunk.withExtraArgument(AsyncMiddleware)];
let enhancer;
if (__DEV__) {
middleware.push(createLogger());
//middleware.push(AsyncMiddleware());
enhancer = compose(
applyMiddleware(...middleware),
devToolsExtension,
);
} else {
enhancer = applyMiddleware(...middleware);
}
initialState.localization = defaultLocalization; //Location
// See https://github.com/rackt/redux/releases/tag/v3.1.0
const store = createStore(rootReducer, initialState, enhancer);
What I am doing wrong? I don't understand the redux-thunk...
I have a Sign Up/Sign in/Sign Out issue. I am creating a React/Redux project, and am using Axios for my GET & POST requests. I am newer to Axios and have read through the documentation to no avail.
Here is the issue.
1. I can successfully create a user with Sign Up in a sign up form.
2. I can make a Post request(a couple of strings) with a basic Redux form.
3. The user and post both are persisting in MongoDB, and I am getting token back.
4. When I log out of that new user, and sign back in with that new user, I am able to sign in, but my post shows up as a 401. Intuition and research tell me that this is an issue with the token, but then again, I'm not sure why I'm able to sign in with the newly created user, but not able to make another post. Any help would be greatly appreciate. Here are my Axios methods:
var config = {
headers: { authorization: localStorage.getItem('token') }
}
export function signinUser({ email, password }){
return function(dispatch){
axios.post(`${ROOT_URL}/signin`, {email, password})
.then(response => {
dispatch({ type: AUTH_USER });
localStorage.setItem('token', response.data.token);
browserHistory.push('/newitem');
})
.catch(response => dispatch(authError("There was a something wrong with your request.")));
}
}
export function signoutUser(){
localStorage.removeItem('token');
return {type: UNAUTH_USER};
}
export function signupUser({ email, password }) {
return function(dispatch) {
// Submit email/password to the server
axios.post(`${ROOT_URL}/signup`, { email, password })
.then(response => {
dispatch({type: AUTH_USER});
//update the token
localStorage.setItem('token', response.data.token);
browserHistory.push('/newitem');
})
.catch(response => dispatch(authError(response.data.error)));
}
}
export function createPost(props) {
return function(dispatch){
axios.post(`${ROOT_URL}/newitem`, { props }, config )
.then(request => {
dispatch({
type: CREATE_POSTS,
payload: request
})
browserHistory.push('/items');
});
}
}