Transform NextJs output into JSON using ExpressJs - node.js

For migration purposes I have to transform the content generated by nextjs into JSON format like {content: "generated markup"} in expressjs.
const express = require('express');
const next = require('next');
const port = 8080;
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({dev});
const handle = nextApp.getRequestHandler();
nextApp.prepare().then(() => {
const server = express();
server.all('*', async (req, res) => {
return handle(req, res);
});
server.use((req, res, next) => {
/* How to set res.json({content:<RESULT_FROM_NEXT_JS>})??? */
});
server.listen(port, () => {
console.log(`[server]: Server is running at http://localhost:${port}`);
});
});
What I understand so far is that next creates a stream of chucked data but I do not know how to block this stream to then create a json from it. Any clue on how to build a middleware for that? Or any other idea on how to generate a JSON in this format?

Related

Returning data to a user from an external API

i am trying to return the value of my search after using the node-spotify-api package to search for an artist.when i console.log the spotify.search ..... without the function search function wrapped around it i get the values on my terminal..what i want is when a user sends a request to the userrouter routes i want is to display the result to the user..i using postman for testing ..
This is the controller
const Spotify = require('node-spotify-api');
const spotify = new Spotify({
id: process.env.ID,
secret: process.env.SECRET,
});
const search = async (req, res) => {
const { name } = req.body;
spotify.search({ type: 'artist', query: name }).then((response) => {
res.status(200).send(response.artists);
}).catch((err) => {
res.status(400).send(err);
});
};
module.exports = {
search,
};
**This is the route**
const express = require('express');
const searchrouter = express.Router();
const { search } = require('./spotify');
searchrouter.route('/').get(search);
module.exports = searchrouter;
**This is my server.js file**
const express = require('express');
require('express-async-errors');
const app = express();
require('dotenv').config();
// built-in path module
const path = require('path');
// port to be used
const PORT = process.env.PORT || 5000;
// setup public to serve staticfiles
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.set('port', PORT);
const searchrouter = require('./route');
app.use('/search', searchrouter);
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'index.html'));
});
app.listen(PORT, (req, res) => {
console.log(`Server is listening on port ${PORT}`);
});
[that is my project structure][1]
Well Your Code has a bug
Which is
searchrouter.route('/').get(search);
You are using a get request and still looking for a req.body
const { name } = req.body;
name is going to equal to = undefined
and when this runs
spotify.search({ type: 'artist', query: name })
it's going to return an empty object or an error
req.body is empty for a form GET request
So Your fix is
change your get request to a post
searchrouter.route('/').post(search);

I want to be redirected to a file.js after when i am logged in

This is my Express code by using handlebars using .hbs code
const express = require("express");
const path = require("path");
const app = express();
const port = 3001;
//for mongo all mongodata
require("dotenv").config();
const cors = require("cors");
app.use(cors());
const Handle = require("./views/mongoschema");
const staticPath= path.join(__dirname, "./views");
app.set("view engine","hbs");
app.use(express.static(staticPath));
//for maintaining the format
app.use(express.json());
//to GET the data in the from the form
app.use(express.urlencoded({extended:false}));
//to render the page this is coming (1st)
app.get("/",(req, res) => {
res.render('index');
});
//for login validation
app.post("/mongoschema",async(req, res) => {
try {
const handleData = new Handle({
title: req.body.one,
description: req.body.two,
url: req.body.four
})
const handled = await handleData.save();
//After REGISTRATION sending the user to index file
***res.status(201).render("index");***
} catch (error) {
res.status(400).send(error);
}
});
app.get("/pages", (req, res) => {
Handle.find({})
.then((items) => res.json(items))
.catch((err) => console.log(err));
});
app.listen(port, () => {
console.log('listening to the port ${port)');
});
the file i want to run now is a "file.js" and index is "index.hbs" it is not able to render the "file.js" how can i render or i will be redirected to the "file.js" file After when my login is SUCCESSFUL.
As someone mentioned in the comments, you should be using:
res.redirect("/"); // path to route as parameter - in this case, index.

Chai testing TypeError: Converting circular structure to JSON

I'm a new learner express.js I want to test simple post and get operations with tdd mechanism. I created the test, route, index and db files but when I try to test POST method it gives me this error.
This is my routes/task.js
const express = require('express');
const router = express.Router();
router.post("/api/task", async (req,res) => {
try {
const task = await new Task(req.body).save();
res.send(task);
} catch (error) {
res.send(error);
}
})
This is my test/task.js
let chai = require("chai");
const chaiHttp = require("chai-http");
const { send } = require("process");
let server = require("../index");
//Assertion Style
chai.should();
chai.use(chaiHttp);
describe('Tasks API', () => {
/**
* Test the POST Route
*/
describe('POST /api/task', () => {
it("It should POST a new task", () => {
const task = {task: "Wake Up"};
chai.request(server)
.post("/api/task")
.send(task)
.end((err, response) => {
response.should.have.status(201);
response.body.should.be.a('string');
response.body.should.have.property('id');
response.body.should.have.property('task');
response.body.should.have.property('task').eq("Wake Up");
response.body.length.should.be.eq(1);
done();
});
});
});
});
This is my db.js
var sqlite3 = require('sqlite3').verbose()
const DBSOURCE = "db.sqlite"
let db = new sqlite3.Database(DBSOURCE, (err) => {
if (err) {
// Cannot open database
console.error(err.message)
throw err
}else{
console.log('Connected to the SQLite database.')
db.run(`CREATE TABLE IF NOT EXISTS todo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task text
)`,
(err) => {
if (err) {
// Table already created
console.log(err);
}
});
}
});
module.exports = db
And this is my index.js
const connection = require('./db');
const express = require('express');
const app = express();
const cors = require("cors");
const port = process.env.PORT || 8080;
app.use(express.json());
app.use(cors());
app.get('/', (req, res) => {
res.send('Hello World');
});
app.post('/api/task', (req, res) => {
res.status(201).send(req);
});
app.listen(port, () => console.log(`Listening on port ${port}...`));
module.exports = app;
The thing that I try to do is building a test case to test the post method. I think I couldn't built the correct relations the files.
Currently, just by doing a POST request to /api/task, the error will appear. That is because of these lines in index.js:
app.post('/api/task', (req, res) => {
res.status(201).send(req);
});
The req parameter is circular, hence cannot be JSON-stringified.
Solution
In routes/task.js export the router:
const express = require('express');
const router = express.Router();
router.post("/api/task", async (req,res) => {
try {
const task = await new Task(req.body).save();
res.send(task);
} catch (error) {
res.send(error);
}
})
// By adding this line you can export the router
module.exports = router
In index.js, include the routes/task.js file and pass it to app.use(...), also remove the now-obsolete /api/task route:
const connection = require('./db');
const express = require('express');
const app = express();
const cors = require("cors");
const taskRoutes = require("./routes/task")
const port = process.env.PORT || 8080;
app.use(express.json());
app.use(cors());
app.get('/', (req, res) => {
res.send('Hello World');
});
app.use(taskRoutes)
app.listen(port, () => console.log(`Listening on port ${port}...`));
module.exports = app;
This way we got rid of the circular structure stringifying and the tests should now pass.

Nextjs and Express as middle ware. How do I set 'localhost:3000/newpage' and 'localhost:3000/newpage/' as the same routes

I am new to express and next and was trying to set 'localhost:3000/newpage' and 'localhost:3000/newpage/' as the same route however as I add a '/' at the end it shows a 404 error.
I am using "next-routes" for dynamic routing and have created routes.js file that looks like this:
const nextRoutes = require("next-routes");
const routes = (module.exports = nextRoutes());
routes.add("index", "/");
routes.add("newpage", "/newpage/:slug"); //with body parser this doesnt work
and my server.js file looks like this:
const express = require("express");
const next = require("next");
const routes = require("./routes");
const dev = process.env.NODE_ENV !== "production";
const port = process.env.PORT || 3000;
const app = next({ dev });
const handle = app.getRequestHandler();
const bodyParser = require("body-parser");
const handler = routes.getRequestHandler(app);
app
.prepare()
.then(() => {
const server = express();
server.use(bodyParser.json()); //with this dynamic routes dont work
server.use (handler); //with this dynamic routes work but / url show 404
server.get("*", (req, res) => {
server.use(handler);
if (req.url.endsWith("/")) {
req.url = req.url.slice(0, -1); // works only when using body parser
}
return handle(req, res);
});
server.listen(port, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
You can modify the url that you get before passing it to Next's handling.
const next = require('next');
const express = require('express');
const routes = require('./routes');
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({dev});
const handle = app.getRequestHandler();
// const handler = routes.getRequestHandler(app); // redundant line
app.prepare().then(() => {
const server = express();
// server.use(handler); // <-- this line is redundant since you need only one handle!
server.get('*', (req, res) => {
if (req.url.endsWith('/')) {
req.url = req.url.slice(0, -1); // remove the last slash
}
return handle(req, res);
});
server.listen(port, (err) => {
if (err) throw err;
console.log('> Ready on http://localhost:3000');
});
});
Working example: https://codesandbox.io/s/express-nextjs-react-c47y8?file=/src/index.js
Navigate to /form or /form/
I had to install the body-parser package then used body-parser. I also changed the folder structure such that I didn't have to import the routes. The final code in server.js looks like this:
const express = require("express");
const next = require("next");
const dev = process.env.NODE_ENV !== "production";
const port = process.env.PORT || 3000;
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
server.get("*", (req, res) => {
if (req.url.endsWith("/")) {
req.url = req.url.slice(0, -1); // remove the last slash
}
return handle(req, res);
});
server.listen(port, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});

NodeJS & GCP PubSub - TypeError: PubSub is not a constructor at Object.<anonymous>

I'm following a tutorial at https://www.woolha.com/tutorials/node-js-google-cloud-pub-sub-basic-examples and having some difficulty..
I've the following code in server.js:-
const express = require('express');
const app = express();
const path = require('path');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');
dotenv.config(); // Reads the .env file from the local folder.
// PubSub constant initialisation
const PubSub = require(`#google-cloud/pubsub`);
const pubsub = new PubSub();
const data = new Date().toString();
const dataBuffer = Buffer.from(data);
const topicName = 'sensehat-led-config';
app.use(bodyParser.urlencoded({ extended: true}));
// Tell the app to use the public folder.
app.use(express.static('public'));
app.get('/', (req,res) => {
res.send('Hello from App Engine!');
})
app.get('/submit', (req, res) => {
res.sendFile(path.join(__dirname, '/views/form.html'));
})
// Need to figure out how to get the css file to work in this. Can't be that hard.
app.get('/sensehat', (req, res) => {
res.sendFile(path.join(__dirname, '/views/sensehat.html'));
})
app.get('/sensehat-publish-message', (req, res) =>{
pubsub
.topic(topicName)
.publisher()
.publish(dataBuffer)
.then(messageId => {
console.log(`Message ${messageId} published`);
})
.catch(err => {
console.error('ERROR:', err);
});
})
app.post('/submit', (req, res) => {
console.log({
name: req.body.name,
message: req.body.message
});
res.send('Thanks for your message!');
})
// Listen to the App Engine-specified port, or 8080 otherwise
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log('Server listening on port ${PORT}...');
})
But when I run it I get a '500 Server Error', and looking at the Stackdriver logs I get the following error:-
TypeError: PubSub is not a constructor at Object.<anonymous>
I'm definitely a newbie at NodeJS and feeling my way around. After reading around I think the issue is coming from the
const PubSub = require(`#google-cloud/pubsub`);
const pubsub = new PubSub();
lines, but no idea how to rectify this.
You can try with latest versions of all libraries.
Dependencies in package.json
"dependencies": {
"#google-cloud/pubsub": "1.5.0",
"google-gax": "1.14.1",
"googleapis": "47.0.0"
}
Example code -
const {
PubSub
} = require('#google-cloud/pubsub');
const pubsub = new PubSub({
projectId: process.env.PROJECT_ID
});
module.exports = {
publishToTopic: function(topicName, data) {
return pubsub.topic(topicName).publish(Buffer.from(JSON.stringify(data)));
},
};
Calling file code
const PubSubPublish = require('path to your above file')
let publishResult = await PubSubPublish.publishToTopic(process.env.TOPIC_NAME, data)
Hope it helps!
You require the default export of #google-cloud/pubsub, but what look for is not in the default export.
Change the way you import PubSub to:
const {PubSub} = require(`#google-cloud/pubsub`);
Instead of:
const PubSub = require(`#google-cloud/pubsub`);

Resources