I have a next.js app and I'm trying to create an api. When I run it as development, the api's get called, but when I run it using next start I get a 404 error when calling the api's.
Here's the relevant server.js code:
app.prepare().then(() => {
require('./server/startup/routes')(server);
server.get('*', (req, res) => {
return handle(req, res);
});
const PORT = process.env.PORT || 5000;
server.listen(PORT, err => {
if (err) throw err;
console.log(`> Read on http://localhost:${PORT}`);
});
});
Here's the routes file
module.exports = app => {
app.use('/api/something-cool', cool);
};
Cool File:
const express = require('express');
const router = express.Router();
router.post('/', async (req, res) => {
...Code
res.send({ status: 'ok' });
});
module.exports = router;
The api route of /something-cool works when I run nodemon, but when I run next run, it returns a 404 error. What am I doing wrong and how can I fix it?
You are using a custom server (express) on top of Next.js to customize routes. This means that first, you have to build the Next.js App and then you have to run your server.js file in order to serve the App.
Option 1:
Builds the production application first
next build
Then run you server.js file:
NODE_ENV=production node server.js
more info here https://github.com/zeit/next.js/tree/master/examples/custom-server-express
Option 2:
There is also the option to create the API route within the Next.js App without using a custom server.
See https://github.com/zeit/next.js/tree/master/examples/api-routes for more info on how to do it.
Related
I have a cli script that I'm converting to a local webapp using express to run a local server and pkg to create an executable that will include all the needed files. I'm using vue for the front-end and during the app development I didn't have any issue. I've tried to do a test build and when I try to launch the app I will get this error message in the opened browser tab Error: cannot GET /.
In my express routes I didn't created the default path for the root / so I suppose that the problem is with this aspect. I've correctly added the express.static() middleware but I'm not sure if I need to send the index.html file that run the vue app using express. How I configure correctly the root endpoint to serve the vue app?
Another small thing about the problem, how I can launch google chrome or default browser in linux using the exec() function of node child_process module?
Can anyone help me please?
async openBrowser(){
this.app.use(express.static(this.assets));
this.app.use(express.json());
this.app.use(express.urlencoded({extended: true}));
http.createServer(this.app).listen(8990, () => {
console.log(chalk.yellowBright('Starting quizist...'));
switch(process.platform){
case 'darwin':
this.child = exec('open --new -a "Google Chrome" --args "http://localhost:8990"');
break;
case 'win32':
this.child = exec('start chrome http://localhost:8990');
break;
}
});
// I have added the root endpoint but it's not clear for me how to serve the vue app
this.app.get('/', (req, res) => {
});
this.app.get('/init', (req, res) => {
this.extractQuestions();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(this.askQuestions));
});
this.app.post('/checkanswer', async (req, res) => {
this.userAnswer = req.body;
const data = await this.checkAnswers();
res.setHeader('Content-Type','applications/json');
res.send(JSON.stringify(data));
});
this.app.get('/results', (req, res) => {
const data = {
userScore: this.score,
questionsNumber: this.askQuestions.length,
answeredQuestions: this.answeredQuestions,
correctAnswers: this.correctAnswers
}
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(data));
});
}
I have an assets folder in which i have images and render them with node express js. Is there a way to secure the assets in such a way that the users access only the assets entitled to them?
const app = express();
app.use('/assets', express.static('assets'));
app.listen(port, () => {
console.log(`Server runnning on port ${port}`);
});
Basically you are looking for authorization middleware which will take care of checking the access.
You can use passport js or any other library or create your own.
Since you are using Express to code, this snippet may be useful.
requireLogin = (req, res, next) => {
if (!req.user) {
return res.status(401).send({ error: 'You must login!' });
}
next();
};
app.get('/assets',requireLogin , express.static('assets'));
Express has very good documentation on middleware here .
I'm trying to post to Heroku from localhost. Here is the sample script at the JavaScript localhost server:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios.post('https://thawing-meadow-45314.herokuapp.com', {}).then(res =>{ //perform operation }) .catch(err=>{ // handel error here. })
Here is Node.js script running on Heroku:
const express = require('express');
const app = express();
const cors = require("cors");
app.use(cors());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.post('/', (req, res) => {
console.log('message is delivered');
}); //app.post
const port = process.env.PORT || 3081;
app.listen(port, () => {
console.log(`Server running on port${port}`);
});
On the command line I use three commands:
Heroku login
Heroku run bash
node index.js
The problem is that nothing happens. Heroku app isn't detecting my post request from the localhost. What I have to do?
axios.post('https://thawing-meadow-45314.herokuapp.com:55058', {})
Don't post to the port Heroku gives you via the PORT environment variables.
That's the internal port your app needs to bind to, but this is exposed publicly as the standard ports 80 and 443. Leave the port out entirely:
axios.post('https://thawing-meadow-45314.herokuapp.com', {})
Also, don't do this:
On the command line I use three commands:
heroku login
heroku run bash
node index.js
Your server should start up automatically via the start script in your package.json (or a custom command given in your Procfile). Something like
"scripts": {
"start": "node index.js"
}
in your package.json should do it. Heroku dynos restart frequently, so it's important that they can start up on their own.
Remove PORT from Axios request URL.
axios.post('https://thawing-meadow-45314.herokuapp.com', {}).then(res =>{
//perform operation
})
.catch(err=>{
// handel error here.
})
Also, make sure you have enabled CORS on your server. I`ll suggest using cors module if you are not using it and add it to your app middleware like this:
const express = require("express");
const app = express();
const cors = require("cors");
app.use(cors());
app.post("/", (req, res) => {
// perform operation and return response.
res.status(200).json({ message: "It worked!" });
});
app.listen(3000, function() {
console.log("server is running");
});
And don`t forget to restart the server after making changes.
I'm attempting to setup a NodeJS application that is using the Next framework to utilize client and server side rendering. I'm trying to get the client and server side rendering to prepend a path to the routes/URLs it generates. The server side render seems to be working by setting up the express server GET function to listen for requests made on route and then passing that along to node by stripping out the prepended route value. However when it comes the rendering on the client the prepended value is missing even when the as="{somestring}" is added to the .js pages for elements like Link so when the external Next javascript files are referenced in the render it's missing the prepended value.
The purpose for the routing is to allow us to run multiple micro-services on one domain each hosted on different instances in AWS and being routed using Target Groups and an ALB.
Essentially what I want to do is replace / with /{somestring} and I need this to be included not only in the server side rendering but in the client side rendering.
URL Example:
www.example.com -> www.example.com/somestring
HTML Render:
www.example.com/_next/960d7341-7e35-4ea7-baf6-c2e7d457f0db/page/_app.js -> www.example.com/somestring/_next/960d7341-7e35-4ea7-baf6-c2e7d457f0db/page/_app.js
Edit/Update
I've tried to use app.setAssetPrefix and while it renders the requests for the assets correctly and the pages load the assets themselves are 404ing.
Here is my server.js file:
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();
app
.prepare()
.then(() => {
// Port
const server = express();
app.setAssetPrefix('test1');
// ======
// Routes
// ======
server.get('/test1/:id', (req, res) => {
const actualPage = `/${req.params.id}`;
const queryParams = { id: req.params.id };
app.render(req, res, actualPage, queryParams);
});
server.get('/test1', (req, res) => {
app.render(req, res, '/');
});
server.get('*', (req, res) => {
handle(req, res);
});
// =============
// End of Routes
// =============
server.listen(port, err => {
if (err) throw err;
console.log(`>Listening on PORT: ${port}`);
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
You need custom routing. Parse the incoming url and replace it with what you want.
Here is is an example to make /a resolve to /b, and /b to /a
https://github.com/zeit/next.js#custom-server-and-routing
I am new to nodejs and did some test on my nodejs app. The code is basically like this.
import express from 'express';
import jsonfile from "jsonfile";
const app = express();
const PORT = process.env.PORT || 3002;
let global=0;
app.use(express.static(`${__dirname}/assets`));
app.use((req, res, next) => {
//write file
const file = '/path/data.json';
var obj = {
name:'cd'
};
jsonfile.writeFile(file,obj,{spaces: 2}, (err)=>{
console.error(err);
})
next();
})
app.get('/test', (req, res, next) => {
global++
res.send(''+global);
})
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}!`);
});
I built it on localhost, and input http://localhost:3002/test into the browser. The app will send the global counter to the browser, but if I refresh the page, I found my nodejs app restart and the counter doesn't change. If I comment out the jsonfile.writeFile part and do the same process like before, the nodejs app won't restart and the counter will increase every time I refresh the page. Why is that?
As you've commented the nodemon forces the page reload (live reload) to enable you to see the changes instantly.
Use node <your_script_name.js> or node <any_command_defined_in_scripts> (whatever is applicable) instead of nodemon if you don't want auto-refresh every time when you refresh the page.
Hope this helps!