How do I manipulate data received from an API endpoint and submit to database - next.js

The aim of my application is to take a URL submitted by a user in a form, pull data from it, manipulate that data, and then submit the manipulated data to a Postgres database.
Current Status
So far I have developed a form on the front end of the application (irrelevant validation / styling code has been removed from this excerpt):
const Feeds = ({ visible }) => {
const handleSubmit = async (e) => {
e.preventDefault();
try {
const body = { feedTitle, websiteUrl, currency, feedUrl };
await fetch('/api/form', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
console.log('body: ', body);
} catch (error) {
console.error(error);
}
};
return (
<Form onSubmit={(e) => handleSubmit(e)} id="myForm" visible={visible}>
<HeaderContainer>
<Header>Add a new feed</Header>
<HeaderDescription>Please complete all of the required fields below and submit to add a new feed.</HeaderDescription>
</HeaderContainer>
<FormContainer>
<InputContainer>
<Label>Feed title</Label>
<Input type="text" placeholder="" value={feedTitle} onChange={(e) => handleChangeFeedTitle(e)} />
</InputContainer>
<InputContainer>
<Label>Website url</Label>
<Input type="text" placeholder="" value={websiteUrl} onChange={(e) => handleChangeWebsiteUrl(e)} />
</InputContainer>
<InputContainer>
<Label>Currency</Label>
<Select onChange={(e) => handleChangeCurrency(e)} name="currency" id="currency-select">
{currencies.map((option, index) => (
<option key={index} value={option.value}>
{option.text}
</option>
))}
</Select>
</InputContainer>
<InputContainer>
<Label>Feed url</Label>
<Input type="text" placeholder="" value={feedUrl} onChange={(e) => handleChangeFeedUrl(e)} />
</InputContainer>
</FormContainer>
{allValid ? <Button type="submit" form="myForm">Save</Button> : <DisabledButton>Save</DisabledButton>}
</Form>
)
};
export default Feeds;
On submission, this POST request hits the /api/form API endpoint:
const handler = async (req, res) => {
const body = req.body;
const response = await fetch(body.feedUrl)
.then(res => res.text())
.then(content => console.log(content))
.catch(err => console.error(err));
console.log('body: ', body);
res.status(200).json({ data: `${body}` })
};
export default handler;
Here I have simply console logged the content coming back from the API. Instead I need to manipulate it using a function and then submit the manipulated data to a database using a separate function.
The Problem
My question is, where should I implement these functions so that they trigger on the server side?
Thanks

Related

NextJS Display API message on the page

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;

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 ?

Fetch file to multer returns undefined

I want to fetch a file via JS to multer but it always returns undefined. I made a postman request and works perfectly and the file saves in '/uploads'. Tried to look into my JS code but don't know whats wrong.
HTML:
<form action="/" class="form" id="form" accept-charset="utf-8" onsubmit="return(validate())">
<div class="form__div">
<label class="form__label" for="image">Send an image (optional)</label>
<input type="file" name="image" id="image">
</div>
<button class="form__submit" type="submit">Submit</button>
</form>
JS:
const validate = () => {
const formData = new FormData(document.querySelector('#form'));
fetch('/send_file', {
method: 'POST',
body: formData.get('image')
})
.then(data => console.log(data))
.catch(err => console.log(err))
return false;
}
Node:
const express = require('express');
const multer = require('multer');
const app = express();
app.use(express.static('./public'));
app.use(express.json());
const storage = multer.diskStorage({
destination: './uploads',
filename: function(req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
const upload = multer({ storage: storage });
app.listen(3000);
app.post('/send_file', upload.single('image'), (req, res) => {
console.log(req.body.image);
res.end();
})
if you are trying to send data using multer, you must put this in your form tag
enctype="multipart/form-data"

How to upload image in angular2? in post method

html
<div class="form-group ">
<input type="file" [(ngModel)]="model.files" name="files" #files="ngModel" class="form-control" id="files" multiple="">
</div>
<div class="form-group ">
<label for="productname">Name</label>
<input type="text" class="form-control" id="productname" required minlength="5" pattern="^[a-zA-Z0-9/,-. ]*$" maxlength="30" [(ngModel)]="model.productname" name="productname" #productname="ngModel">
</div>
<div class="form-group ">
<label for="sales">Sales price/rate</label>
<input type="text" class="form-control" id="sales" pattern="[0-9]+" required minlength="0" maxlength="10" [(ngModel)]="model.sales" name="sales" #sales="ngModel">
</div>
<button type="submit" class="btn btn-success " (click)="save(productname,sales,files);onChangeroot(root)">Submit</button>
component.ts
export class NewProductComponent {
productservice:ProductsService
selectedFile = null;
onfileSelected(event){
console.log(event);
this.selectedFile =<File>event.target.files[0];
}
save1(productname,sales,files)
{
let obj = {
'pro_name':productname.value,
'sales':sales.value,
'image':files.value
}
var json = JSON.stringify(obj)
console.log(json)
const fd = new FormData();
fd.append('image', this.selectedFile , this.selectedFile.name );
this.http.post('http://127.0.0.1:8000/product/images/',fd)
this.service.save_user1(json).subscribe(response => console.log('Inserted Successfully'),
error => console.log(error));
}
}
service.ts
export class ProductsService {
save_user1(exp_data:any): Observable<any[]>{
console.log("console",exp_data)
let headers = new Headers({ 'Content-Type': 'application/json'});
let options = new RequestOptions({ headers: headers });
console.log("options",options)
return this.http.post("http://127.0.0.1:8000/product/", exp_data, options)
.map(response => response.json())
.catch(error => Observable.throw(error.statusText));
};
}
After selecting image in console event is working while submitting the form except that image all other data are so successfully stored in database.
I am tried in this way but not able to post image,.so please help me how to do that ?
I think your service method need to be change. Usually we have to send file as multipart form data. My working copy is :
let formData: FormData = new FormData();
formData.append('file', this.imageFile);
this.http.post(this.url , formData) .subscribe(
(result) => {
console.log(result);
},
(error) => {
console.error(error);
}
);

redux-form not showing errors

In my redux-form I'm having troubles showing the errors back to the form,
here's my form:
import React, { Component } from "react";
import { connect } from "react-redux";
import {
registerUser,
signOutUser
} from "../../actions/redux-token-auth-config";
import { Field, reduxForm, SubmissionError } from "redux-form";
import { FormDiv, SubmitButton, Title, Working } from "./styles";
const renderField = ({ input, label, type, meta: { touched, error } }) =>
<div>
<label>
{label}
</label>
<div>
<input {...input} placeholder={label} type={type} />
{touched &&
error &&
<span>
{error}
</span>}
</div>
</div>;
const errorsFrom = response => response.response.data;
const signUp = (data, dispatch, props) => {
return dispatch(props.registerUser(data))
.then(response => dispatch(signOutUser(data)))
.catch(response => {
throw new SubmissionError(errorsFrom(response));
});
};
const SignUp = props => {
const { error, handleSubmit, pristine, submitting } = props;
return (
<FormDiv>
<Title>subscribe</Title>
<form onSubmit={handleSubmit(signUp)}>
<Field
name="email"
component={renderField}
type="text"
placeholder="Email"
label="Email"
/>
<br />
<Field
name="password"
component={renderField}
type="password"
placeholder="Password"
label="Password"
/>
<br />
<Field
name="passwordConfirmation"
component={renderField}
type="password"
placeholder="Confirm Password"
label="Confirm Password"
/>
<br />
{error &&
<strong>
{error}
</strong>}
<SubmitButton type="submit" disabled={pristine || submitting}>
Sign Up
</SubmitButton>
{submitting && <Working />}
</form>
</FormDiv>
);
};
const ReduxFormSignUp = reduxForm({
form: "signup"
})(SignUp);
const mapStateToProps = (state, props) => {
return {
error:
state.form.signup && state.form.signup.submitErrors
? state.form.signup.submitErrors.errors
: null
};
};
const mapDispatchToProps = dispatch => {
return {
registerUser
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ReduxFormSignUp);
After submitting I get some errors in my redux state:
but for some reason they fail to be passed back in the props. so my props contain handleSubmit, pristine, submitting but no error prop. Regardless whether I use my mapStateToProps or not
Update
There seems to be an incongruence between the documentation and the library, I see no reference to passing errors to fields in here https://github.com/erikras/redux-form/blob/master/src/createField.js, as it's described here: https://redux-form.com/7.2.3/docs/api/field.md/#usage
Similarly here: https://redux-form.com/7.2.3/docs/api/reduxform.md/ I see no references to passing error prop to the wrapped component. But I'm seeing so many examples where I should expect an error prop passed by reduxForm.
Any clues?
Each field error is passed to the Field and the generic error (_) is passed to the form. I think your code should work. Are the fields touched?

Resources