Skip to content

Commit

Permalink
getServerSideProps should support props value as Promise (#28607)
Browse files Browse the repository at this point in the history
Previous to this change, getServerSideProps could only return plain objects
for props, e.g.:

```javascript
export async function getServerSideProps() {
  return {
    props: {
      text: 'some value',
    }
  }
}
```

With this commit, the props object can also be a Promise, e.g.

```javascript
export async function getServerSideProps() {
  return {
    props: (async function () {
      return {
        text: 'promise value',
      }
    })(),
  }
}
```

For now, the framework simply waits for the results of the props Promise to resolve,
but this small change sets the groundwork for later allowing props to be streamed (cc @devknoll).

## Feature

- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. -- *This is part of @devknoll's ongoing work to support streaming.*
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not. *not sure if this applies here*
- [ ] Errors have helpful link attached, see `contributing.md`
  • Loading branch information
kara committed Aug 30, 2021
1 parent 8c553e8 commit 9be387c
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/basic-features/data-fetching.md
Expand Up @@ -672,7 +672,7 @@ The `context` parameter is an object containing the following keys:

`getServerSideProps` should return an object with:

- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization)
- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) or a Promise that resolves to a serializable object.
- `notFound` - An **optional** boolean value to allow the page to return a 404 status and page. Below is an example of how it works:

```js
Expand Down
4 changes: 4 additions & 0 deletions packages/next/server/render.tsx
Expand Up @@ -804,6 +804,10 @@ export async function renderToHTML(
;(renderOpts as any).isRedirect = true
}

if ((data as any).props instanceof Promise) {
;(data as any).props = await (data as any).props
}

if (
(dev || isBuildTimeSSG) &&
!isSerializableProps(
Expand Down
15 changes: 15 additions & 0 deletions test/integration/getserversideprops/pages/promise/index.js
@@ -0,0 +1,15 @@
export async function getServerSideProps() {
return {
props: (async function () {
return {
text: 'promise',
}
})(),
}
}

export default ({ text }) => (
<>
<div>hello {text}</div>
</>
)
11 changes: 11 additions & 0 deletions test/integration/getserversideprops/test/index.test.js
Expand Up @@ -136,6 +136,12 @@ const expectedManifestRoutes = () => [
slug: 'slug',
},
},
{
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/promise.json$`
),
page: '/promise',
},
{
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/refresh.json$`
Expand Down Expand Up @@ -523,6 +529,11 @@ const runTests = (dev = false) => {
expect(data.pageProps.post).toBe('post-1')
})

it('should return data correctly when props is a promise', async () => {
const html = await renderViaHTTP(appPort, `/promise`)
expect(html).toMatch(/hello.*?promise/)
})

it('should navigate to a normal page and back', async () => {
const browser = await webdriver(appPort, '/')
let text = await browser.elementByCss('p').text()
Expand Down

0 comments on commit 9be387c

Please sign in to comment.