NextJS Display API message on the page - next.js

I have a login form. The user types their username/password into the form and submits it. The form uses fetch() to send a request to the backend API and get a response back.
I'd like to display a message from the API on the page. It'll be {apiErrorMessage}
For example, the user's account could be locked. The Password could be wrong. The email address could not be confirmed yet.
In the old days I would do this using Partial Page updates AJAX using C# Razor pages.
This is a NextJS project and have no idea how to do this.
Should I be using Server Side Rendering?
Should I be using Client Side Rendering?
UseEffect() hook?
I'd still like the page to look good for SEO purposes.
I'm thinking maybe we have to use a second page to do this with SSR? LoginResult page or something?
Any Help Appreciated :)
Thanks!
const Login = () => {
let apiErrorMessage = "";
const loginFormSubmit = async (event) => {
event.preventDefault()
// Get data from the form.
const data = {
username: event.target.username.value,
password: event.target.password.value,
}
const JSONdata = JSON.stringify(data)
const response = await fetch(`/api/login`)
const result = await response.json()
if (response.ok){
router.push('/dashboard');
}
else {
// Get the error message from API
apiErrorMessage = result.message;
}
}
return (
<div>
<form onSubmit={loginFormSubmit}>
<div className="mb-4">
<label htmlFor="username"><b>Username</b></label>
<input type="text" id="username" required/>
</div>
<div className="mb-4">
<label htmlFor="password"><b>Password</b></label>
<input type="password" id="password" required/>
</div>
<button type="submit">Login</button>
</form>
</div>
<div>
<p>{apiErrorMessage}</p>
</div>
)
}
export default Login;

you don't need to create a page for that you can create a simple useState hook :
const [errorMsg,setErrorMsg] = useState('');
when the user fails to login :
else {
// Get the error message from API
setErrorMsg(result.message)
}
Now under your <form> you create a <div> that will be only shown when errorMsg !== '' :
{errorMsg === '' ? '' : (<div>
<p>login failed : ${errorMsg}</p>
</div>)}
as simple as that.

I can't think of a reason you'd need good SEO for a login component that just accepts user input and returns an error response or forwards them to a page.
Since everything inside /pages is a Client component by default, I think your best bet is simply using state and effects. Here's an example with some slight modifications to your code:
import {useState} from 'react';
import { useRouter } from 'next/navigation';
const Login = () => {
const [apiErrorMessage, setApiErrorMessage] = useState('');
const router = useRouter();
const loginFormSubmit = async (event: any) => {
event.preventDefault();
// Get data from the form.
const data = {
username: event.target.username.value,
password: event.target.password.value,
};
const JSONdata = JSON.stringify(data);
const response = await fetch(`/api/login`);
const result = await response.json();
if (response.ok) {
router.push("/dashboard");
} else {
// Get the error message from API
setApiErrorMessage(result.message);
}
};
return (
<div>
<div>
<form onSubmit={loginFormSubmit}>
<div className="mb-4">
<label htmlFor="username">
<b>Username</b>
</label>
<input type="text" id="username" required />
</div>
<div className="mb-4">
<label htmlFor="password">
<b>Password</b>
</label>
<input type="password" id="password" required />
</div>
<button type="submit">Login</button>
</form>
</div>
<div>
<p>{apiErrorMessage}</p>
</div>
</div>
);
};
export default Login;

Related

Next js fetch API query

const Fetch = ({nationalize}) => {
return (
<div>
<h1>Nationalize</h1>
<h5>
<h4>You're {nationalize.name} and here's your results</h4>
{nationalize.country.map((i)=>{
return(
<div key={i.country_id}>
<h5>{(i.probability)*100}% {i.country_id} </h5>
</div>
)
})}
</h5>
</div>
);
}
export const getStaticProps = async (ctx) => {
const res = await fetch('https://api.nationalize.io?name=joe')
const nationalize = await res.json()
return {
props:{
nationalize,
}
}
}
export default Fetch;
So this is my NextJs page fetch API from nationalize.io, the API takes a name as a query. The code works just fine but I wanted to take the name query from an input field instead of being set manually. In this example it is Joe 'https://api.nationalize.io?name=joe', any suggestion? thank you
You use getStaticProps which pre-builds the page before sending it to your browser, so all data inside getStaticProps has to be static as the name says, and not being taken from any user input or query params.
If you'd like someone to change the name, you can either render it on the server using getServerSideProps and taking the name from the params, or fetch it on client-side via a user input as you mentioned:
import { useState } from "react";
const Fetch = () => {
const [name, setName] = useState("");
const [nationalize, setNationalize] = useState();
const submitName = async (name) => {
const res = await fetch("https://api.nationalize.io?name=" + name);
const nationalizeJson = await res.json();
setNationalize(nationalizeJson);
};
return (
<div>
<h1>Nationalize</h1>
{nationalize && (
<>
<h4>You're {nationalize.name} and here's your results</h4>
{nationalize.country.map((i) => {
return (
<div key={i.country_id}>
<h5>
{i.probability * 100}% {i.country_id}{" "}
</h5>
</div>
);
})}
</>
)}
<div>
<input
id="input"
type="text"
placeholder="Enter your name"
value={name}
onChange={(e) => setName(e.target.value)}
style={{ padding: "10px" }}
/>
<button
onClick={() => {
if (!name) return;
submitName(name);
}}
>
Submit
</button>
</div>
</div>
);
};
export default Fetch;
Here's how it works:
Let user enter his name with a controlled text input (https://reactjs.org/docs/forms.html#controlled-components) and store the value in the state name
When a user clicks the "Submit" button, call the api as you did in getStaticProps but take the name from the state name, and store the returned JSON from the api in the state nationalize.
In the JSX part, check if nationalize is defined, and only then display the information fetched from the api.
This is obviously just a demo and could use some optimization like styling or a loading indicator.

Next.js secure authenticaton

I am trying to make a secure authentication with next.js
I've followed this article because it shows how do to it with api route and proxy server, but I do not understand why he uses this http-proxy module
I did the same thing without it and it seems to work perfectly fine.
Here is my pages/login.js file
import { Button } from '#mui/material'
import { useState } from 'react'
export default function loginPage() {
const [message, setMessage] = useState();
const handleSubmit = async (event) => {
event.preventDefault();
const data = {
username: event.target.username.value,
password: event.target.password.value
};
const response = await fetch('/api/login', {
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(data),
});
setMessage(response.message);
}
return (
<div className="block">
<form onSubmit={handleSubmit}>
{message ? <p className="message">{message}</p> : ""}
<label htmlFor="first">Username</label>
<input type="text" id="username" name="username" variant="outlined" required />
<label htmlFor="last">Password</label>
<input type="text" id="password" name="password" variant="outlined" required />
<div>
<Button className="btn" type="submit" variant="outlined">Submit</Button>
</div>
</form>
</div>
)
}
Here is my pages/api/[...catchAll].js
import Cookies from "cookies"
export default async function handler(req, res) {
console.log("HERE");
const response = await fetch("http://localhost:7000/register", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(req.body)
}).then((res) => res.json());
const cookies = new Cookies(req, res);
cookies.set("authorization", response.token, {
httpOnly: true,
sameSite: "lax",
maxAge: 100000
});
res.status(200).json(response)
}
My front-end pages/login.js sends a request to pages/api[...catchAll].js and then a request to my back-end is being made. My back-end returns
{
message: "success",
token: crypto.randomBytes(48).toString('hex')
}
and then pages/api/[...catchAll].js sets my cookies. Why in the article the person uses the httpProxy module ? Is it more secure ? Why ?
I've seen this technique in a lot of places because of secure measurements, but I do not understand why they use this proxy server.
Could someone please explain ?

The email address is badly formatted firebase vue.js

Getting an error "The email address is badly formatted." when trying to use Vue.js with firebase to create a login page.
Here's my code:
<template>
<div class = "sign-up">
<p> Let's create a new account</p>
<input type="email" v-model="email" placeholder="Email"> <br>
<input type="password" v-model="password" placeholder="Password"> <br>
<button v-on:click="signUp">Sign Up</button>
<br>
</div>
</template>
<script>
import firebase from 'firebase'
export default {
name:'Signup',
data: function() {
return {
email: '',
password: '',
}
},
methods: {
signUp: function() {
firebase.auth().createUserWithEmailAndPassword(this.email, this.password).then(
function (user) {
alert('Your account has been created')
},
function(error) {
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
alert(errorMessage);
}
console.log(error);
});
}
}
}
</script>
I did make sure that I have enabled the authentication part at the firebase console .
Don't know why still get this error
Help please
Thank God I solved it.
The problem is solved by adding
firebase.initializeApp(config);
right after
import firebase from 'firebase'
since I have already initialize Firebase in other files
the problem might be caused by javascript loading asynchronously .
This works well. I tired to solve your ones. I have brought official firebase auth sample. Your user was not defined and while importing you must have use {} to prevent .auth() error.
<template>
<div class = "sign-up">
<p> Let's create a new account</p>
<input type="email" v-model="email" placeholder="Email">
<input type="password" v-model="password" placeholder="Password">
<button v-on:click="signUp">Sign Up</button>
</div>
</template>
<script>
import {fb} from '../firebase';
export default {
name:'Signup',
data() {
return {
email: "",
password: "",
}
},
methods: {
signUp: function() {
fb.auth().createUserWithEmailAndPassword(this.email, this.password)
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
alert(errorMessage);
}
console.log(error);
});
}
}
}
</script>

Intercept an async action in redux

I'm building a simple notes app using react with redux. My create notes action looks like this:
import axios from "axios";
const URL = 'http://localhost:3002/api/';
export function createNote(data = {}) {
return {
type: 'CREATE_NOTE',
payload: axios.post(URL + 'notes/', data),
};
}
And I've got the following in my reducer:
// Create
case 'CREATE_NOTE_PENDING':
{
return {
...state,
creating: true
};
}
case 'CREATE_NOTE_FULFILLED':
{
return {
...state,
creating: false,
notes: action.payload.data
};
}
case 'CREATE_NOTE_REJECTED': {
return {
...state,
creating: false,
error: action.payload
};
}
And this is my Notes class:
function mapStateToProps(store) {
return {
data: store.note.notes,
fetching: store.note.fetching,
creating: store.note.creating,
error: store.note.error,
};
}
class Notes extends Component {
componentWillMount() {
this.props.dispatch(fetchNotes());
}
create() {
this.props.dispatch(createNote({
title: this.refs.title.value,
content: this.refs.content.value,
}));
this.refs.title.value = '';
this.refs.content.value = '';
}
render() {
return (
<div className="App">
<h1 className="text-center">Notepad</h1>
<div className="row">
<div className="col-md-6 col-md-offset-3">
<div className="form-group">
<input ref="title" placeholder="Create a note" className="form-control" disabled={this.props.creating} />
</div>
<div className="form-group">
<textarea ref="content" className="form-control" disabled={this.props.creating} />
</div>
<button onClick={this.create.bind(this)}
type="submit"
className="btn btn-primary"
style={{float: 'right'}}
disabled={this.props.creating} >Save</button>
</div>
</div>
No on create I'm going to disable the form till I get an answer from the server and reset the content inside the form. My question is how to reset the content of the form when I've got response, i.e. when the form is enabled?
If you want to control the content of input and textarea, you should use controlled components. That is to provide a value attribute to, e.g. input
<input value={this.state.title} placeholder="Create a note" className="form-control" disabled={this.props.creating} />
You can use either internal state or redux state for the value. Then you are able to control the value by setState or redux actions.

Using param in redux form hidden field

I am looking to add reset password ability to an auth app in redux. The server sends out an email and puts a token in the params. I am trying to use a hidden field to store the param value that will need to be submitted back to the server. The problem is redux form doesn't seem to be picking up the value and is passing undefined. I am alittle new to redux form and know this isnt yet at v6. My plan is to get all the functionality for the auth built first then upgrade
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import * as actions from '../../actions';
class Resetpassword extends Component {
componentWillMount() {
this.props.clearErrorMsg();
}
handleFormSubmit(formProps) {
// Call action creator to sign up the user!
this.props.resetPassword(formProps);
}
renderAlert() {
if (this.props.errorMessage) {
return (
<div className="alert alert-danger">
<strong>Oops!</strong> {this.props.errorMessage}
</div>
);
}
}
render() {
const { handleSubmit, fields: { password, passwordConfirm, token }} = this.props;
console.log(this.props.location.query.reset);
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<fieldset className="form-group">
<label>Password:</label>
<input className="form-control" {...password} type="password" />
{password.touched && password.error && <div className="error">{password.error}</div>}
</fieldset>
<fieldset className="form-group">
<label>Confirm Password:</label>
<input className="form-control" {...passwordConfirm} type="password" />
{passwordConfirm.touched && passwordConfirm.error && <div className="error">{passwordConfirm.error}</div>}
</fieldset>
<input className="form-control" value={this.props.location.query.reset} {...token} type="hidden" />
{this.renderAlert()}
<button action="submit" className="btn btn-primary">Sign up!</button>
</form>
);
}
}
function validate(formProps) {
const errors = {};
if (!formProps.password) {
errors.password = 'Please enter a password';
}
if (!formProps.passwordConfirm) {
errors.passwordConfirm = 'Please enter a password confirmation';
}
if (formProps.password !== formProps.passwordConfirm) {
errors.password = 'Passwords must match';
}
return errors;
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error };
}
export default reduxForm({
form: 'resetpassword',
fields: ['password', 'passwordConfirm','token'],
validate
}, mapStateToProps, actions)(Resetpassword);
The solution I found which may not follow best practices is to remove the hidden field and pass the query string prop into the action handleFormSubmit
handleFormSubmit(formProps) {
// Call action creator to sign up the user!
this.props.resetPassword(formProps,this.props.location.query.reset);}
Then having the action deal with it

Resources