Skip to content

Commit

Permalink
fixup! Throw error if res is accessed after gSSP returns
Browse files Browse the repository at this point in the history
  • Loading branch information
kara committed Sep 13, 2021
1 parent 71185e2 commit ed15673
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 6 deletions.
4 changes: 2 additions & 2 deletions errors/gssp-no-mutating-res
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Must not access ServerResponse after getServerSideProps() returns
# Must not access ServerResponse after getServerSideProps() resolves

#### Why This Error Occurred

`getServerSideProps()` surfaces a `ServerResponse` object through the `res` property of its `context` arg. This object is not intended to be accessed or changed after `getServerSideProps()` returns.
`getServerSideProps()` surfaces a `ServerResponse` object through the `res` property of its `context` arg. This object is not intended to be accessed or changed after `getServerSideProps()` resolves.

This is because the framework tries to optimize when items like headers or status codes are flushed to the browser. If they are changed after `getServerSideProps()` completes, we can't guarantee that the changes will work.

Expand Down
7 changes: 4 additions & 3 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -745,11 +745,12 @@ export async function renderToHTML(
let canAccessRes = true
let resOrProxy = res
if (process.env.NODE_ENV !== 'production') {
resOrProxy = new Proxy(res, {
get: function (obj: ServerResponse, prop: string, receiver: any) {
resOrProxy = new Proxy<ServerResponse>(res, {
get: function (obj, prop, receiver) {
if (!canAccessRes) {
throw new Error(
`Must not access ServerResponse after getServerSideProps() returns! https://nextjs.org/docs/messages/gssp-no-mutating-res`
`You should not access 'res' after getServerSideProps resolves.` +
`\nRead more: https://nextjs.org/docs/messages/gssp-no-mutating-res`
)
}
return Reflect.get(obj, prop, receiver)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

export async function getServerSideProps(context) {
return {
props: (async function () {
// Mimic some async work, like getting data from an API
await new Promise((resolve) => setTimeout(resolve))
context.res.setHeader('test-header', 'this is a test header')
return {
text: 'res',
}
})(),
}
}

export default ({ text }) => {
return (
<>
<div>hello {text}</div>
</>
)
}
16 changes: 15 additions & 1 deletion test/integration/getserversideprops/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ const expectedManifestRoutes = () => [
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/promise\\/mutate-res.json$`
),
page: '/promise/mutate-res',
},
{
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/promise\\/mutate-res-props.json$`
),
page: '/promise/mutate-res-props',
},
{
dataRouteRegex: normalizeRegEx(
Expand Down Expand Up @@ -721,9 +727,17 @@ const runTests = (dev = false) => {
it('should show error for accessing res after gssp returns', async () => {
const html = await renderViaHTTP(appPort, '/promise/mutate-res')
expect(html).toContain(
'Must not access ServerResponse after getServerSideProps() returns'
`You should not access 'res' after getServerSideProps resolves`
)
})

it('should show error for accessing res through props promise after gssp returns', async () => {
const html = await renderViaHTTP(appPort, '/promise/mutate-res-props')
expect(html).toContain(
`You should not access 'res' after getServerSideProps resolves`
)
})

} else {
it('should not fetch data on mount', async () => {
const browser = await webdriver(appPort, '/blog/post-100')
Expand Down

0 comments on commit ed15673

Please sign in to comment.