I'm trying to create a routing function for some products. The home screen works fine (displaying all the products), but I'm trying to add functionality so that when a user clicks on the product it automatically redirects to a page which pulls information about that products.
When I run my back end server (npm start) I get this in my terminal and an undefined response when I click on my product:
[nodemon] 2.0.12
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): backend/**/*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `babel-node backend/server.js`
serve at http://localhost:5000 [Function (anonymous)]
I've tried going through my code a few times and playing around with things to see if there's an issue with a function but couldn't see an issue.
This is the code for my server.js:
import express from 'express';
import cors from 'cors';
import { products } from './data.js';
const app = express();
app.use(cors());
app.get('/api/products', (_req, res) => {
res.send(products);
});
app.get('/api/products/:id')
app.listen(5000, () => {
console.log('serve at http://localhost:5000', (req, res) => {
const product = data.products.find((x) => x._id === req.params.id)
if (product) {
res.send(product);
} else {
res.status(404).send({ message: 'Product Not Found' })
}
})
});
This is my config.js:
import axios from 'axios'
import { apiUrl } from "./config"
export const getProduct = async (id) => {
try {
const response = await axios({
url: `${apiUrl}/api/products/${id}`,
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
if (response.statusText !== 'OK') {
throw new Error(response.data.message);
}
return response.data;
} catch (err) {
console.log(err);
return { error: err.message }
}
};
I've just included my util.js and the product screen code below just for a better understanding of what I'm doing, however I think the issue would be in the server.js or config.js.
util.js:
export const parseRequestUrl = () => {
const url = document.location.hash.toLowerCase();
const request = url.split('/');
return {
resource: request[1],
id: request[2],
action: [3],
};
}
productscreen.js
import { getProduct } from '../api';
import { parseRequestUrl } from '../util'
const ProductScreen = {
render: async () => {
const request = parseRequestUrl();
const product = await getProduct(request.id);
return `<h1>${product.name}</h1>`;
}
}
export default ProductScreen;
You are literally printing the function with console.log by providing it as a second argument. Move the handler function to the appropriate route handler for /api/products/:id, as shown below:
import express from 'express';
import cors from 'cors';
import { products } from './data.js';
const app = express();
app.use(cors());
app.get('/api/products', (_req, res) => {
res.send(products);
});
// The handler function is moved here from `listen` callback.
app.get('/api/products/:id', (req, res) => {
const product = data.products.find((x) => x._id === req.params.id);
if (product) {
res.send(product);
} else {
res.status(404).send({ message: 'Product Not Found' });
}
})
app.listen(5000, () => {
console.log('serve at http://localhost:5000');
});
Related
so i try to run a function using node scheduler but it seems that the scheduler not work at all, here is my try:
import express from "express";
import dotenv from "dotenv";
import connection from "./connection.js";
import cors from "cors";
import indexRouter from "./routes/index.js";
import mailfunc from "./libraries/emailNotif.js";
import checkAnswer from "./libraries/checkAnswer.js";
import schedule from "node-schedule";
try {
schedule.scheduleJob("1 * * * *", () => {
console.log("check Answer");
checkAnswer();
});
const env = dotenv.config().parsed;
var app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
cors({
origin: env.CORS_URL,
})
);
app.use("/", indexRouter);
// catch 404
app.use((req, res, next) => {
res.status(404).json({ message: "404_NOT_FOUND" });
});
// error handler
app.use((req, res, next) => {
if (env.NODE_ENV == "production") {
// render the error page
res.status(500).json({ message: "REQUEST_FAILED" });
} else {
//development error handler
next();
}
});
//db connection
connection();
app.listen(env.APP_PORT, () => {
console.log(`Server is running on port ${env.APP_PORT}`);
});
////// coba push
} catch (error) {
mailfunc(error.toString());
}
Here is the function i want to call after 1 minute....,
import mailfunc from "../libraries/emailNotif.js";
import Answer from "../models/Answer.js";
const checkAnswer = async () => {
try {
function toJSONLocal() {
var local = new Date();
return local.toLocaleDateString("en-SE");
}
console.log(toJSONLocal());
const answer = Answer.aggregate([
{ $match: { date: toJSONLocal() } },
{ $group: { _id: "$title", count: { $sum: 1 } } },
]);
console.log("====================================");
console.log(answer);
console.log("====================================");
if (!answer) {
return mailfunc("Database might be down");
}
if (answer.length == 0) {
return mailfunc("No data found, Please Remind your team to fill the OKP");
}
} catch (err) {
mailfunc(err.toString());
}
};
export default checkAnswer;
can someone tell me where did i do wrong here.. because no error show up on the terminal, i try to change the minute of the schedule task but i dont trigger the function
I am working on a MERN stack project for a proof-of-concept and am creating a wall where I can add posts from a form. The form accepts name, tags, title, and a message along with an image. Upon clicking submit, Axios gives me a 404 error in the console.
For example, trying to POST this data (using a mongoose schema):
{creator: 'asdfasddd', title: 'asdfasd', message: 'asdffdd', tags: 'dddd', selectedFile: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA0gA…AAAAAAAAAAAAAAAAAYI+9AisfoxArdwtiAAAAAElFTkSuQmCC'}
And I receive this error message:
{message: 'Request failed with status code 404', name: 'AxiosError', code:
'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
This is the AxiosError expanded only once, I can expand the error further if that would help, I've looked through it an found nothing in particular that seems helpful.
[
If somebody could tell me why I am getting this error besides the fact that I am timing out and what would cause me to that would be helpful. One note to help is that I receive some interaction to my DB just no data (of course). My POST functions are here if that helps as well, but I don't believe that is wrong either. If I need to post my the code to my component itself let me know.
import * as api from '../api';
// Action Creators
export const getPosts = () => async (dispatch) => {
try {
const { data } = await api.fetchPosts();
dispatch( {type: 'FETCH_ALL', payload: data });
} catch (error) {
console.log(error.message);
}
}
export const createPost = (post) => async (dispatch) => {
try {
const {data } = await api.createPost(post);
dispatch({type: 'CREATE', payload: data});
} catch (error) {
console.log(post);
console.log(error.toString());
console.log(error);
}
}
Code for POSTS for the back end in my controllers folder.
import PostMessage from '../models/postMessage.js';
export const getPosts = async (req, res) => {
try {
const postMessages = await PostMessage.find();
console.log(postMessages);
res.status(200).json(postMessages);
} catch (error) {
res.status(404).json({ message: error.message});
}
}
export const createPost = async (req, res) => {
const post = req.body;
const newPost = new PostMessage(post);
try {
await newPost.save();
res.status(201).json(newPost);
} catch (error) {
res.status(409).json({ message:error.message});
}
}
Within api folder:
import axios from 'axios';
const url = 'http://localhost:5000/posts';
//const port = process.env.PORT;
//const url = 'http://localhost:3000/posts';
export const fetchPosts = () => axios.get(url);
export const createPost = (newPost) => axios.post(url, newPost);
How the data is routed server-side:
import express from 'express';
import { getPosts, createPost } from '../controllers/posts.js';
const router = express.Router();
//http://localhost:5000/posts
router.get('/', getPosts);
router.get('/', createPost);
export default router;
In your router file, you are missing the POST method
router.post('/', createPost);
In your main file, it should be
app.use("/posts", postRouter)
Whenever I try to run the function refreshStock() in an endpoint in one of the API endpoints /api/seller/deactivate it gives me this error:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (net.js:1318:16)
at listenInCluster (net.js:1366:12)
at Server.listen (net.js:1452:7)
at C:\Users\***\Documents\GitHub\***\***\.next\server\pages\api\seller\deactivate.js:191:10
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command
It looks like it's trying to restart the server, but it happens after it compiles, is there something I'm doing wrong, I've followed a couple of tutorials on medium, and they give this same type of code, just not ES Modules. I want to use ES Modules because it is what my database functions are written in.
Server.js:
import express from 'express';
import { createServer } from 'http';
import next from 'next';
import models from './server/models';
import { genStock } from './server/lib/functions';
import { Server } from 'socket.io';
const port = parseInt(process.env.PORT || '3000', 10);
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const nextHandler = nextApp.getRequestHandler();
const app = express();
const server = createServer(app);
const io = new Server(server);
const Users = models.users;
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
if (!socket.handshake.auth.token) return next(err);
let user = await Users.findOne({
where: {
socket_token: socket.handshake.auth.token,
},
});
if (!user) {
console.log('unauthenticated socket');
socket.disconnect();
next(err);
}
await Users.update(
{ socket_id: socket.id },
{
where: {
socket_token: socket.handshake.auth.token,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
// Works fine
const stock = await genStock();
socket.emit('updateStock', stock);
});
// Fails with address already in use :::3000
export async function refreshStock() {
const stock = await genStock();
io.emit('updateStock', stock);
}
nextApp.prepare().then(async () => {
app.all('*', (req, res) => nextHandler(req, res));
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
This is meant to refresh the stock after a seller deactivates their account and sends all users the new stock.
/api/seller/deactivate
....
await refreshStock();
....
I figured it out, I just split up the WebSocket server and the next.js one. I have whitelisted local IPs that may appear to only allow server-to-server communication. Although I don't think this is full-proof as there is most likely a better way to have this type of communication but for now it works.
/**
* This server cannot be imported in /api folders, it won't work.
* Although it can import other functions
* */
import express from 'express';
import { createServer } from 'http';
import session from 'express-session';
import { Server } from 'socket.io';
import { genStock } from './server/lib/stockFunctions';
import { sessionStore } from './server/lib/session';
import passport from './server/lib/passport';
import models from './server/models';
const authorizedIPs = ['::1', '127.0.0.1', '::ffff:127.0.0.1'];
const Users = models.users;
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: `http://localhost:3000`,
methods: ['GET', 'POST'],
credentials: true,
},
});
const wrap = (middleware) => (socket, next) => middleware(socket.request, {}, next);
io.use(
wrap(
session({
secret: "---",
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
},
store: sessionStore,
}),
),
);
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
const user = socket.request.user;
if (!user) return next(err);
await Users.update(
{ socket_id: socket.id },
{
where: {
id: user.id,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
const stock = await genStock();
socket.emit('updateStock', stock);
});
app.post('/refresh-stock', async function (req, res) {
const ip = req.ip;
if (!authorizedIPs.includes(ip)) {
console.log(ip);
return res.status(401).json({ success: false });
}
const newStock = await genStock();
io.emit('updateStock', newStock);
return res.status(200).json({ success: true });
});
httpServer.listen(3001);
console.log(`> Websockets ready on http://localhost:3001`);
I have built my portfolio webpage with next.js now I need to test it. to test the express server I use supertest. But the problem is I need to refactor express to use it. Because supertest need to access to app() before listening.
I started the way how I used to implement in node.js app. Put the express code in app.js and call it in index.js.
const express = require("express");
const server = express();
const authService = require("./services/auth");
const bodyParser = require("body-parser");
//put all the middlewares here
module.exports = server;
and then in index.js
const server = require("express")();
// const { parse } = require("url");
const next = require("next");
const routes = require("../routes");
const path = require("path");
require("./mongodb");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
// const handle = app.getRequestHandler(); //this is built in next route handler
const handle = routes.getRequestHandler(app);
app
.prepare()
.then(() => {
const server = require("./app");
//I required this outside too but it did not solve the issue
server.listen(3000, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
with this set up, express is listening, I am able connect to mongodb, during the start up there is no issue.
When i request to localhost:3000, there is no response from localhost, it is spinning till timeout
Create a test client:
// test-client.ts
import { createServer, RequestListener } from "http";
import { NextApiHandler } from "next";
import { apiResolver } from "next/dist/next-server/server/api-utils";
import request from "supertest";
export const testClient = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
return apiResolver(
req,
res,
undefined,
handler,
{
previewModeEncryptionKey: "",
previewModeId: "",
previewModeSigningKey: "",
},
false
);
};
return request(createServer(listener));
};
Test your APIs with:
// user.test.ts
import viewerApiHandler from "../api/user";
import { testClient } from "../utils/test-client";
const request = testClient(viewerApiHandler);
describe("/user", () => {
it("should return current user", async () => {
const res = await request.get("/user");
expect(res.status).toBe(200);
expect(res.body).toStrictEqual({ name: "Jane Doe" });
});
});
For those who want to add query parameters, here's the answer:
import { createServer, RequestListener } from 'http'
import { NextApiHandler } from 'next'
import { apiResolver } from 'next/dist/server/api-utils/node'
import request from 'supertest'
export const handlerRequester = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
let query = {}
let queryUrl = req.url.split('?')[1]
if (queryUrl) {
queryUrl
.split('&')
.map((p) => [p.split('=')[0], p.split('=')[1]])
.forEach((k) => {
query[k[0]] = k[1]
})
}
return apiResolver(
req,
res,
query,
handler,
{
previewModeEncryptionKey: '',
previewModeId: '',
previewModeSigningKey: '',
},
false
)
}
const server = createServer(listener)
return [request(server), server]
}
I've just released a new npm package which handle this case here:
https://www.npmjs.com/package/nextjs-http-supertest
Feel free to test it and give me feedback !
I have a 404.jade file that I want to render whenever there is an invalid GET request.
Here is my current code:
app.js
import Koa from 'koa'
import views from 'koa-views'
import serve from 'koa-static'
import rootRoutes from './routes/index'
import userRoutes from './routes/user'
const app = new Koa()
app.use(views(`${__dirname}/views`, { extension: 'jade' }))
app.use(serve(`${__dirname}/public`))
app.use(rootRoutes.routes())
app.use(userRoutes.routes())
app.listen(3000, () => {
console.log('Server running at http://localhost:3000')
})
export default app
routes/index.js
import Router from 'koa-router'
const router = new Router()
router.get('/', async ctx => {
await ctx.render('index')
})
router.get('/about', async ctx => {
await ctx.render('about')
})
export default router
routes/user.js
import Router from 'koa-router'
const router = new Router({ prefix: '/user' })
router.get('/:name', async ctx => {
const user = ctx.params.name
await ctx.render('user', { user })
})
export default router
How can I handle any type of invalid GET request and somehow use await ctx.render('404') whenever it happens?
You can add a custom middleware in your app.js file.
import Koa from 'koa'
import views from 'koa-views'
import serve from 'koa-static'
import rootRoutes from './routes/index'
import userRoutes from './routes/user'
const app = new Koa()
app.use(async(ctx, next) => {
try {
await next()
const status = ctx.status || 404
if (status === 404) {
ctx.throw(404)
}
} catch (err) {
ctx.status = err.status || 500
if (ctx.status === 404) {
//Your 404.jade
await ctx.render('404')
} else {
//other_error jade
await ctx.render('other_error')
}
}
})
app.use(views(`${__dirname}/views`, { extension: 'jade' }))
app.use(serve(`${__dirname}/public`))
app.use(rootRoutes.routes())
app.use(userRoutes.routes())
app.listen(3000, () => {
console.log('Server running at http://localhost:3000')
})
export default app
The default value of ctx.response.status is 404
application.js line 125 :
callback() {
const fn = compose(this.middleware);
if (!this.listeners('error').length) this.on('error', this.onerror);
const handleRequest = (req, res) => {
res.statusCode = 404; // defaul
const ctx = this.createContext(req, res);
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fn(ctx).then(handleResponse).catch(onerror);
};
return handleRequest;
}
and if you call :
this.render('index',{});
this.send();
this.body='';
the status code will change automatically.
So we can just use this :
app.use(async (ctx, next) => {
if(parseInt(ctx.status) === 404){
ctx.status = 404
ctx.body = {msg:'emmmmmmm, seems 404'};
}
})
Warning here, if you are using koa-router, make sure the function above is called with app.use( app = new Koa() ), not router.use