Reputation: 11020
I want a server side generated page in next.js to be served as a file. So I wanted to grab the rendered content inside a custom server.js file:
const express = require('express');
const next = require('next');
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({dev});
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.get('/', async (req, res) => {
const nextResponse = await app.renderToHTML(req, res, '/', req.query);
console.log('nextResponse', nextResponse);
console.log('res.body', res.body);
});
server.get('*', (req, res) => {
return handle(req, res);
});
server.listen(port, (err) => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
Oddly enough every console.log returns null or undefined.
I thought that renderToHTML
would just return the rendered HTML string. Is there any way to do this?
Upvotes: 2
Views: 4187
Reputation: 1
Reference Gregory Jakubowicz solution, nextjs 14.2.5
no handleCompression method, but can use the new handleCatchallRenderRequest
instead of, The following code snippet has been tested locally:
const _handleCatchallRenderRequest =
nextServer.handleCatchallRenderRequest.bind(nextServer);
nextServer.handleCatchallRenderRequest = (
req: any,
res: any,
parsedUrl: any,
) => {
_handleCatchallRenderRequest(req, res, parsedUrl);
const _resEnd = res._res.end.bind(res._res);
res._res.end = function (payload: any) {
// payload contains the html content
return _resEnd(payload);
};
};
Upvotes: 0
Reputation: 41
I adapted Nikola Knežević solution for NextJS 13.2.3, and it does the job :
const { parse } = require('url')
const next = require('next')
const express = require('express')
const dev = process.env.NODE_ENV !== 'production'
const hostname = 'localhost'
const port = 3000
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port, dir: '<absolute root path to your project>' })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('*', async (req, res) => {
const nextServer = await app.getServer()
const _handleCompression = nextServer.handleCompression.bind(nextServer)
nextServer.handleCompression = (req, res) => {
_handleCompression(req, res)
const _resEnd = res._res.end.bind(res._res)
res._res.end = function (payload) {
// payload contains the html content
return _resEnd(payload)
}
}
return handle(req, res, parsedUrl)
})
server.listen(port, (err) => {
if (err) {
throw err
}
console.log(`> Ready on http://localhost:${port}`)
})
})
Upvotes: 0
Reputation: 97
This one is a bit tricky but achievable.
The idea is to override res.end
function in order to catch rendered HTML there. The tricky part is that Next.js gzips and streams response using the compression library that's overriding res.end
somewhere in the middle of the handle
function.
The compression library is initialized using the handleCompression
function of the Next.js's Server object (which is accessible using the app.getServer()
), so that function needs to get overridden too.
So it should be looking something like this:
const { parse } = require('url');
const next = require('next');
const express = require('express');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const port = process.env.PORT || 3000;
const handle = app.getRequestHandler();
app.prepare()
.then(() => {
const server = express();
server.get('*', async (req, res) => {
const parsedUrl = parse(req.url, true);
const nextServer = await app.getServer();
const _handleCompression = nodeServer.handleCompression.bind(nodeServer);
nextServer.handleCompression = (req, res) => {
_handleCompression(req, res);
const _resEnd = res.end.bind(res)
res.end = function (payload) {
console.log('Rendered HTML: ', payload);
return _resEnd(payload);
}
}
return handle(req, res, parsedUrl);
});
server.listen(port, err => {
if (err) throw err;
console.log('> Ready on http://localhost:' + port);
});
});
After you get rendered HTML you don't have to use the saved _resEnd
function. Feel free to manipulate, serve as a file, or whatever you want with it.
Upvotes: 1