I'm wondering if the following scenario is possible with serverless (deployed on Vercel) Next JS:
I have a route /product/[id].tsx
when you send a request with the header Accept: text/html
, I would like it to go through the normal Next JS flow with a React page. But, when a request is given with Accept: application/json
, I would like it to return the JSON representation.
I have acplished it by using some custom middleware I wrote for Express (see below), but I would also like to deploy it on Vercel, and Vercel is not designed to work with a custom Express implementation.
So the question is, is it possible to do this in a serverless environment? Could one of the Next JS React pages return pure JSON, or could you return React from a Next JS Api route? Or is there another way to acplish this?
server.ts
import express, { Request, Response } from "express";
import next from 'next'
import { parse } from 'url'
const port = parseInt(process.env.PORT || "3000", 10);
const dev = process.env.NODE_ENV !== "production";
async function run(): Promise<void> {
const app = express();
const nextApp = next({ dev })
const nextHandler = nextApp.getRequestHandler()
await nextApp.prepare()
app.use((req, res) => {
res.format({
"text/html": async (req, res) => {
const parsedUrl = parse(req.url!, true)
await this.handler(req, res, parsedUrl)
},
"application/json": async (req, res) => {
res.json({
"dummy": "data"
})
}
})
});
app.listen(port, () => {
console.log(
`> Server listening at http://localhost:${port} as ${
dev ? "development" : process.env.NODE_ENV
}`
);
});
}
run()
I'm wondering if the following scenario is possible with serverless (deployed on Vercel) Next JS:
I have a route /product/[id].tsx
when you send a request with the header Accept: text/html
, I would like it to go through the normal Next JS flow with a React page. But, when a request is given with Accept: application/json
, I would like it to return the JSON representation.
I have acplished it by using some custom middleware I wrote for Express (see below), but I would also like to deploy it on Vercel, and Vercel is not designed to work with a custom Express implementation.
So the question is, is it possible to do this in a serverless environment? Could one of the Next JS React pages return pure JSON, or could you return React from a Next JS Api route? Or is there another way to acplish this?
server.ts
import express, { Request, Response } from "express";
import next from 'next'
import { parse } from 'url'
const port = parseInt(process.env.PORT || "3000", 10);
const dev = process.env.NODE_ENV !== "production";
async function run(): Promise<void> {
const app = express();
const nextApp = next({ dev })
const nextHandler = nextApp.getRequestHandler()
await nextApp.prepare()
app.use((req, res) => {
res.format({
"text/html": async (req, res) => {
const parsedUrl = parse(req.url!, true)
await this.handler(req, res, parsedUrl)
},
"application/json": async (req, res) => {
res.json({
"dummy": "data"
})
}
})
});
app.listen(port, () => {
console.log(
`> Server listening at http://localhost:${port} as ${
dev ? "development" : process.env.NODE_ENV
}`
);
});
}
run()
Share
Improve this question
asked Jul 10, 2020 at 18:20
jaxoncreedjaxoncreed
1,1282 gold badges14 silver badges25 bronze badges
1 Answer
Reset to default 8Thanks to @timneutkens (Lead engineer for NextJS) on Twitter we have our answer. It is possible. You use getServerSideProps
and perform everything you want to the res
object there. Just be sure to end it with .end
so it skips the react rendering phase.
I've made a sample repo for the code here: https://github./jaxoncreed/nextjs-content-type-test
And here's the page that matters:
import Head from 'next/head'
export default function Home() {
return (
<div className="container">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1>Test App</h1>
</main>
</div>
)
}
export async function getServerSideProps({ req, res }) {
if (req.headers.accept === "application/json") {
res.setHeader('Content-Type', 'application/json')
res.write(JSON.stringify({ "dummy": "data" }))
res.end()
}
return {
props: {}, // will be passed to the page ponent as props
}
}