Next.js form works locally but not on live server - javascript

I have been implementing a Next.js app for a side project of mine. It is a basic brochure-style site with a contact form.
The form works perfectly fine when the site is run locally, however I have just published the site to Netlify and now when submitting a form I encounter the following error:
POST https://dux.io/api/form 404
Uncaught (in promise) Error: Request failed with status code 404
at e.exports (contact.js:9)
at e.exports (contact.js:16)
at XMLHttpRequest.d.(/contact/anonymous function) (https://dux.io/_next/static/cFeeqtpSGmy3dLZAZZWRt/pages/contact.js:9:4271)
Any help would be extremely appreciated!
This is my Form Submit function:
async handleSubmit(e) {
e.preventDefault();
const { name, email, option, message } = this.state;
const form = await axios.post('/api/form', {
name,
email,
option,
message
});
this.setState(initialState);}
This is my server.js file:
const express = require('express');
const next = require('next');
const bodyParser = require('body-parser');
const mailer = require('./mailer');
const compression = require('compression');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.use(compression());
server.use(bodyParser.json());
server.post('/api/form', (req, res) => {
const { email = '', name = '', option = '', message = '' } = req.body;
mailer({ email, name, option, text: message })
.then(() => {
console.log('success');
res.send('success');
})
.catch(error => {
console.log('failed', error);
res.send('badddd');
});
});
server.get('*', (req, res) => {
return handle(req, res);
});
server.listen(3000, err => {
if (err) throw err;
console.log('> Read on http://localhost:3000');
});
});

It looks like nextjs tries to render the /api/form page and you get a not found with that.
Please make sure you start the server with node server.js instead of next start.

What about try to use full endpoint http://~~~/api/form instead of just /api/form?
Or I think, you can solve this problem if you use process.env
const config = {
endpoint: 'http://localhost:8080/api'
}
if (process.env.NODE_ENV === 'production') {
config.endpoint = 'hostname/api'
}

Related

CRUD operations using mongoose and express

I am creating an express app using mongoose with the intention of connecting this to React for the frontend.
I have listed some CRUD operations for a customer controller below but there are a few things I do not like about this approach.
When using Customer.findById with a valid ObjectID that is not found, it returns null with a 200 response code. I want this to return 404 if no customer was found. I realise I could change the catch response to a 404, but I want to have some generic error handling incase the server goes down during the request or an invalid ObjectId was provided, which brings me to my next item.
If I provide an invalid ObjectId I want to provide some meaningful message, is 500 the right response code?
Error handling: Am I returning errors the correct way? currently errors return a string with the error message. Should I return JSON instead? e.g. res.status(500).json({error: error.message). I am planning on connecting this to react (which I am still learning) and I assume the UI will need to display these messages to the user?
findById is repeated in getCustomerById, updateCustomer, and deleteCustomer. I feel this is bad practice and there must be a more streamlined approach?
I want to have one function that validates if the ObjectId is valid. I am aware that I can do this is the routes using router.params but I'm not sure if checking for a valid id should be in the routes file as it seems like something the controller should be handling? See routes example below from another project I did.
What are the best practices and suggested ways to improve my code, based on the above?
I have read the documentation from mongoose, mozilla, and stackoverflow Q&A but they don't seem to address these issues (at least I could not find it).
I am really after some guidance or validation that what I am doing is correct or wrong.
customer.controller.js
const Customer = require("../models/customer.model");
exports.getCustomers = async (req, res) => {
try {
const customers = await Customer.find();
res.status(200).json(customers);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.getCustomerById = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.addCustomer = async (req, res) => {
try {
const customer = new Customer(req.body);
await customer.save().then(res.status(201).json(customer));
} catch (error) {
res.status(500).send(error.message);
}
};
exports.updateCustomer = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
Object.assign(customer, req.body);
customer.save();
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.deleteCustomer = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
await customer.remove();
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
Router.params example
This is a routes file (not related to my current app) and is provided as an example of how I have used router.params in the past.
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Artist = require("../models/Artist");
const loginRequired = require("../middleware/loginRequired");
const {
getArtists,
addArtist,
getArtistById,
updateArtist,
deleteArtist,
} = require("../controllers/artistController");
router
.route("/")
.get(loginRequired, getArtists) // Get all artists
.post(loginRequired, addArtist); // Create a new artist
router
.route("/:id")
.get(loginRequired, getArtistById) // Get an artist by their id
.put(loginRequired, updateArtist) // Update an artist by their id
.delete(loginRequired, deleteArtist); // Delete an artist by their id
router.param("id", async (req, res, next, id) => {
// Check if the id is a valid Object Id
if (mongoose.isValidObjectId(id)) {
// Check to see if artist with valid id exists
const artist = await Artist.findOne({ _id: id });
if (!artist) res.status(400).json({ errors: "Artist not found" });
res.locals.artist = artist;
res.locals.artistId = id;
next();
} else {
res.status(400).json({ errors: "not a valid object Id" });
}
});
module.exports = router;
i personly like to make error handeling more global so i would write something like
constPrettyError = require('pretty-error')
const pe = new PrettyError()
const errorHandler = (err, req, res, next) => {
if (process.env.NODE_ENV !== 'test') {
console.log(pe.render(err))
}
return res
.status(err.status || 500)
.json({ error: { message: err.message || 'oops something went wrong' } })
}
module.exports = errorHandler
as a handler
the in your index / server file
app.use(errorHandler)
then in your handlers just
} catch (err) {
next(err);
}
as an example
if (!artist) next({ message: "Artist not found" ,status:404 });
also, note that you can customize this error handler to switch case (or object) a custom error per status as well if you want
const errorHandler = (err, req, res, next) => {
if (process.env.NODE_ENV !== 'test') {
console.log(pe.render(err))
}
const messagePerStatus = {
404: 'not found',
401: 'no authorization'
}
const message = messagePerStatus[err.status]
return res
.status(err.status || 500)
.json({
error: { message: message || err.message || 'oops something went wrong' }
})
}
then just
if (!artist) next({status:404 });
I also agree with answer by Asaf Strilitz but still need to show what i do in my projects
Create a custom error class
AppError.js
class AppError extends Error {
constructor(statusCode, message) {
super();
// super(message);
this.statusCode = statusCode || 500 ;
this.message = message || "Error Something went wrong";
}
}
module.exports = AppError;
Create an error handling middleware
errors.js
const AppError = require("../helpers/appError");
const errors = (err, req, res, next) => {
// console.log(err);
let error = { ...err };
error.statusCode = error.statusCode;
error.message = error.message;
res.status(error.statusCode).json({
statusCode: err.statusCode,
message: err.message,
});
};
exports.errors = errors;
Create a middleware to validate object id
validateObjectId.js
const mongoose = require("mongoose");
const AppError = require("appError");
module.exports = function (req, res, next) {
const { _id } = req.params;
if (_id && !mongoose.Types.ObjectId.isValid(_id)) {
throw new AppError(422, "Invalid ID field in params");
}
next();
};
In your app.js
const { errors } = require("errors");
// At the end of all middlewares
// Error Handler Middleware
app.use(errors);
In your routes file
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Artist = require("../models/Artist");
const loginRequired = require("../middleware/loginRequired");
const validateId = require("validateObjectId");
const {
getArtists,
addArtist,
getArtistById,
updateArtist,
deleteArtist,
} = require("../controllers/artistController");
// Your routes
router
.route("/:id")
.get(validateId, loginRequired, getArtistById) // Get an artist by their id
.put(validateId, loginRequired, updateArtist) // Update an artist by their id
.delete(validateId, loginRequired, deleteArtist); // Delete an artist by their id
module.exports = router;
Now regarding findById method being repeated i dont see anything bad in that as it is specific to database call still you can introduce a staic method on model itself or create a single method on cntroller but still need to check if it returns the found object or not and handle the error on that.

Why Expres Async Errors send different result as expected

I found a nice npm package by the name of Express Async Errors which according to the documentation, it's really nice to use.
However, if I implement it, the server will crash.
Here is my Route handler code
Controller
const { Genre } = require("../models");
const { StatusCodes } = require("http-status-codes");
const getGenre = async (req, res) => {
const genre = await Genre.findById({ _id: req.params.id });
if (!genre) {
return res.status(StatusCodes.BAD_REQUEST).json({
message: "The genre with the given ID was not found.",
});
}
res.status(StatusCodes.OK).json({ status: "success", data: genre });
};
*router*
const express = require("express");
const { authenticate, admin } = require("../middleware");
const router = express.Router();
const { schemaValidator } = require("../middleware");
const validateRequest = schemaValidator(true);
const { genres } = require("../controllers");
const { getAllGenres, getGenre, createGenre, updateGenre, deleteGenre } =
genres;
.route("/genres")
.get(getAllGenres)
Main Application Entry
require("express-async-errors");
//Routers
const routers = require("./router");
const connectDB = require("./DB/connect");
const express = require("express");
const app = express();
app.use(config.get("URI"), routers);
app.use(notFoundMiddleware);
const start = async () => {
const port = process.env.PORT || 3000;
const connectionString = config.get("mongoDB.connString");
await connectDB(connectionString)
.then(() => DBdebug(`Connected to MongoDB: ${connectionString}`))
.catch(() => console.log("MongoDB connection failure"));
app.listen(port, () => debug(`Listening on port ${port}...`));
};
start();
Above code is imported into index.js together with express-async-errors.
According to the document, if I create an error, express-async-errors has to handle this without crashing the application. My question is what I'm doind wrong???
I shut down the Mongo-driver just to create a scenario that the server is down with a status 503.
MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017
at Function.Model.$wrapCallback (/Volumes/Seagate/lib/model.js:5087:32)
at /Volumes/Seagate/lib/query.js:4510:21
at /Volumes/Seagate/node_modules/mongoose/lib/helpers/promiseOrCallback.js:32:5
From previous event:
at promiseOrCallback (/Volumes/Seagate/node_modules/mongoose/lib/helpers/promiseOrCallback.js:31:10)
at model.Query.exec (/Volumes/Seagate/node_modules/mongoose/lib/query.js:4509:10)
at model.Query.Query.then (/Volumes/Seagate/node_modules/mongoose/lib/query.js:4592:15)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
Instead of above error, I should see "Something went wrong" as the default message in Postman without crashing the application.
Can someone point me to the right direction?
NB: The link which I had used us is https://www.npmjs.com/package/express-async-errors
Thank you very much in advanced.
After two days of researching my problem, I finally convinced my self that the problem was me and found a solution for this particular matter.
I have created an ErrorHandlerMiddleware and in the particular middleware I check if the error is an instance of MongooseError object, if this is the case, I just send for now an custom message.
See code below:
const { StatusCodes } = require("http-status-codes");
const { CustomApiError } = require("../errors");
const Mongoose = require("mongoose");
const errorHandlerMiddleware = (err, req, res, next) => {
console.log("errorMiddleWare");
if (err instanceof CustomApiError) {
return res.status(err.statusCode).json({ message: err.message });
}
if (err instanceof Mongoose.Error) {
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
message: "There is a technical problem, please try again later",
});
}
};
module.exports = { errorHandlerMiddleware };
In main application entry, I just pass errorHandlerMiddleware as a argument reference in app.use.
See code below
require("express-async-errors");
//Routers
const routers = require("./router");
const connectDB = require("./DB/connect");
const express = require("express");
const app = express();
app.use(config.get("URI"), routers);
app.use(notFoundMiddleware);
app.use(errorHandlerMiddleware);
const start = async () => {
const port = process.env.PORT || 3000;
const connectionString = config.get("mongoDB.connString");
await connectDB(connectionString)
.then(() => DBdebug(`Connected to MongoDB: ${connectionString}`))
.catch(() => console.log("MongoDB connection failure"));
app.listen(port, () => debug(`Listening on port ${port}...`));
};
start();
And this is the final result in PostMan:
If there are any comments regarding this solution, feel free to do this.
By comments and can learn more!!!
Thank you in advanced and keep coding!!!!

Node Api Doesn't Respond To Postman or through Browser (No Status Code) But on Terminal It showed no error

I Am developing a Node API for employee department model
for which i developed couple of Request Like 'GET PUSH PATCH DELETE' (CRUD Operation)
for both end employee & department
i am developing on my local server
here's my app.js looks like
const http=require('http');
const app =require('./app');
const port =process.env.PORT || 3000;
const server = http.createServer(app);
server.listen(port);
console.log('Server Has Been Started');
console.log('localhost:'+port);
And On the listner side here's my app.js looks like
// Dependency
const express = require('express');
const app = express();
const morgan =require('morgan');
const bodyParser =require('body-parser');
const mongoose =require('mongoose');
// Importing routes
const employeeRoutes =require('./api/routes/employee');
const departmentRoutes =require('./api/routes/department');
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
// Header Defination to Avoid Corp's
app.use((req, res ,next) =>
{
res.header("Access-Control-Allow-Origin","*");
res.header(
"Access-Control-Allow-Headers",
"Origin,X-Requested-With,Content-Type,Accept,Authorization"
);
if (req.method === 'OPTIONS')
{
res.header('Access-Control-Allow-Methods','PUT,POST,PATCH,DELETE,GET');
return res.status(200).json({});
}
});
// Routes for api
app.use('/employee',employeeRoutes);
app.use('/department',departmentRoutes);
// Error Handling
app.use((req, res, next) =>
{
const error =new Error('Not Found');
error.status =404;
next(error);
});
app.use((error, req, res, next) =>
{
res.status(error.status ||500);
res.json({
error:{
message:error.message
}
});
});
module.exports = app;
in order to send request and receiving response as a json on other end
i developed employee .js and department .js with GET PUSH PATCH DELETE
here's my both file looks like
./api/routes/employee.js
it just showcase script to validate code is working or not
const express = require('express');
const router =express.Router();
const mongoose =require('mongoose');
const Employee =require('../models/Employee');
router.get('/',(req, res ,next) => {
res.status(200).json({
message:'Handling GET request to ./employees'
});
});
router.post('/',(req, res ,next) => {
res.status(201).json({
message:'Handling POST request to ./employees',
});
});
router.get('/:employeeId',(req, res ,next) =>
{
const id =req.params.employeeId;
if (id === 'special'){
res.status(200).json({
message: 'You Discovered A speacial ID',
id:id
});
} else
{
res.status(200).json({
message:'You passed an ID'
});
}
});
router.patch('/:employeeId',(req, res ,next) =>
{
res.status(200).json({
message:'Updated Employees'
});
});
router.delete('/:employeeId',(req, res ,next) =>
{
res.status(200).json({
message:'Deleted Employee'
});
});
module.exports =router;
../api/routes/department.js
const express = require('express');
const router =express.Router();
router.get('/',(req, res ,next) =>{
res.status(200).json({
message:'Department fetched'
});
});
router.post('/',(req, res ,next) =>{
const department={
name:req.body.name,
Id:req.body.Id
};
res.status(201).json({
message:'Department created',
createdDepartment:department
});
});
router.get('/:departmentId',(req, res ,next) =>{
res.status(200).json({
message:'Department Details',
departmentId: req.params.departmentId
});
});
router.delete('/:departmentId',(req, res ,next) =>{
res.status(200).json({
message:'Department Deleted',
departmentId: req.params.departmentId
});
});
router.patch('/:departmentId',(req, res ,next) =>
{
res.status(200).json({
message:'Department Updated',
departmentId :req.params.departmentId
});
});
module.exports =router;
Now when i try to run this code in as permy coding experience it has no logical error
also on terminal it showed
terminal o/p
but when i try to run in postman
it just loading not response is coming back
likepostman o/p
and also on web like
web o/p
and then onto terminal be like
terminal after get not respond still server is running
i know there is problem with connection but i double check all the possible way like proxy and firewall disabled
also i tried to delter node module and package.json(lock) and reinstalled it nothing of them is working
and when i stop the server postman show socket hangup
sockethangup postman
i wanna to know where i am lacking with meaninf ful answer
Add next() to pass the request to the next middleware after the CORS section.
// Header Defination to Avoid Corp's
app.use((req, res ,next) =>
{
res.header("Access-Control-Allow-Origin","*");
res.header(
"Access-Control-Allow-Headers",
"Origin,X-Requested-With,Content-Type,Accept,Authorization"
);
if (req.method === 'OPTIONS')
{
res.header('Access-Control-Allow-Methods','PUT,POST,PATCH,DELETE,GET');
return res.status(200).json({});
}
// Pass next middleware
next();
});

How do I query with url parameter?

I am new to Node.js and trying to check if an e-mail is already taken by sending the email as a url parameter from iOS app. It is not working, not sure what I am doing wrong.
I am unable to console.log the email parameter in VSCode sent from the front-end, it DOES print in XCODE ( http://localhost:3000/api/user/email/test#gmail.com ) and I know the backend is getting the GET request.
My router code is:
const express = require(`express`)
const router = new express.Router()
const User = require(`../models/user-model`) // import User model
router.get(`/api/user/email/:email`, async (req, res) => {
console.log(req.params) // does NOT print email: test#gmail.com
try {
const user = await User.findOne(req.params.email)
if (user) {
console.log(user._id)
res.send({ available: false })
} else {
res.send({available: true})
}
} catch {
res.status(404).send()
}
})
Thank you!
const express = require(`express`)
const app = new express();
app.get(`/api/user/email/:email`, async (req, res) => {
console.log(req.params) // does NOT print email: test#gmail.com
try {
// const user = await User.findOne(req.params.email)
const user = {_id:123};
if (user) {
console.log(user._id)
res.send({ available: false })
} else {
res.send({available: true})
}
} catch {
res.status(404).send()
}
})
app.listen(3000,function(){
console.log("running");
})
Editing this.. I dont have enough points to comment.. your route seems to be fine, maybe you are not telling your application to use this route, somewhere before starting your application you should have something like:
this.app = new express();
...
this.app.use('/api', MailRouter); //<=== Adding your required mail route
...
I use to split url one parte here (/api) and the other one in the router (/user/email/:email). I'm not sure how to do it by adding it fully to the router (Maybe '/' maybe '')

Network Error when connecting React frontend to

The app is using next.js connected to express, which is in turn connected to AWS MySql.
I'm trying to initially load some products I have stored on my database. However, i get the following network error:-
TypeError: NetworkError when attempting to fetch resource.
I troubleshooted the frontend code by using an external api and that works fine, so it's something to do with the express middleware.
See code excerpts below...
index.js
import '../scss/style.scss';
import NavBar from '../components/Navbar/Navbar';
import fetch from 'isomorphic-unfetch';
const Index = (props) => (
<div>
<NavBar />
<h1>See our products below yeah</h1>
</div>
)
Index.getInitialProps = async function() {
const res = await fetch('localhost:3000/');
const data = await res.json();
return {
posts: data
}
}
export default Index;
server.js
const express = require('express');
const next = require('next');
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const db = require('../lib/db')
const escape = require('sql-template-strings')
app.prepare().then(() => {
const server = express()
server.get('/', (req, res, next) => {
let sql = 'SELECT * FROM products'
db.query(sql, [], (err, rows) => {
if (err) {
throw err;
} else {
return res.json({
data: rows
})
}
})
})
...
db.js
const mysql = require('serverless-mysql')
const db = mysql({
config: {
host: "XXX",
database: "XXX",
user: "XXX",
password: "XXX"
}
})
exports.query = async query => {
try {
const results = await db.query(query)
await db.end()
return results
} catch (error) {
return { error }
}
}
whenever you have api request from a different server you have to setup CORS on both frontend and backend
Link: https://expressjs.com/en/resources/middleware/cors.html

Categories

Resources