Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: better cookies API for Edge Functions #36478

Merged
merged 11 commits into from May 9, 2022

Conversation

Kikobeats
Copy link
Member

@Kikobeats Kikobeats commented Apr 26, 2022

This PR introduces a more predictable API to manipulate cookies in an Edge Function context.

const response = new NextResponse()

// set a cookie
response.cookies.set('foo, 'bar') // => set-cookie: 'foo=bar; Path=/'`

// set another cookie
response.cookies.set('fooz, 'barz') // => set-cookie: 'foo=bar; Path=/, fooz=barz; Path=/'`

// delete a cookie means mark it as expired
response.cookies.delete('foo') // => set-cookie: 'foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT, fooz=barz; Path=/'`

// clear all cookies means mark all of them as expired
response.cookies.clear() // => set-cookie: 'fooz=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT, foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT'`

This new cookies API uses Map interface, and it's available for NextRequest and NextResponse.

Additionally, you can pass a specific cookies option as a third argument in set method:

response.cookies.set('foo', 'bar', {
  path: '/',
  maxAge: 60 * 60 * 24 * 7,
  httpOnly: true,
  sameSite: 'strict',
  domain: 'example.com'
}

Note: maxAge it's in seconds rather than milliseconds.

Any cookie manipulation will be reflected over the set-cookie header, transparently.

closes #31719

@Kikobeats Kikobeats changed the title feat: better cookies API for NextResponse feat: better cookies API for Edge Functions Apr 26, 2022
@Kikobeats Kikobeats force-pushed the edge/cookies branch 3 times, most recently from 55f1ba3 to 9d0fe01 Compare April 27, 2022 09:21
@ijjk
Copy link
Member

ijjk commented Apr 27, 2022

Stats from current PR

Default Build (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary Kikobeats/next.js edge/cookies Change
buildDuration 15.6s 15.8s ⚠️ +242ms
buildDurationCached 6.2s 6.2s -28ms
nodeModulesSize 475 MB 475 MB ⚠️ +5.38 kB
Page Load Tests Overall decrease ⚠️
vercel/next.js canary Kikobeats/next.js edge/cookies Change
/ failed reqs 0 0
/ total time (seconds) 3.895 3.944 ⚠️ +0.05
/ avg req/sec 641.92 633.91 ⚠️ -8.01
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.337 1.35 ⚠️ +0.01
/error-in-render avg req/sec 1870.55 1851.53 ⚠️ -19.02
Client Bundles (main, webpack)
vercel/next.js canary Kikobeats/next.js edge/cookies Change
925.HASH.js gzip 179 B 179 B
framework-HASH.js gzip 42 kB 42 kB
main-HASH.js gzip 28.8 kB 28.8 kB
webpack-HASH.js gzip 1.44 kB 1.44 kB
Overall change 72.5 kB 72.5 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary Kikobeats/next.js edge/cookies Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary Kikobeats/next.js edge/cookies Change
_app-HASH.js gzip 1.36 kB 1.36 kB
_error-HASH.js gzip 193 B 193 B
amp-HASH.js gzip 308 B 308 B
css-HASH.js gzip 327 B 327 B
dynamic-HASH.js gzip 2.69 kB 2.69 kB
head-HASH.js gzip 359 B 359 B
hooks-HASH.js gzip 920 B 920 B
image-HASH.js gzip 5.7 kB 5.7 kB
index-HASH.js gzip 263 B 263 B
link-HASH.js gzip 2.64 kB 2.64 kB
routerDirect..HASH.js gzip 320 B 320 B
script-HASH.js gzip 391 B 391 B
withRouter-HASH.js gzip 318 B 318 B
85e02e95b279..7e3.css gzip 107 B 107 B
Overall change 15.9 kB 15.9 kB
Client Build Manifests
vercel/next.js canary Kikobeats/next.js edge/cookies Change
_buildManifest.js gzip 459 B 459 B
Overall change 459 B 459 B
Rendered Page Sizes
vercel/next.js canary Kikobeats/next.js edge/cookies Change
index.html gzip 532 B 532 B
link.html gzip 546 B 546 B
withRouter.html gzip 528 B 528 B
Overall change 1.61 kB 1.61 kB

Default Build with SWC (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary Kikobeats/next.js edge/cookies Change
buildDuration 17.8s 17.9s ⚠️ +96ms
buildDurationCached 6.3s 6.3s -55ms
nodeModulesSize 475 MB 475 MB ⚠️ +5.38 kB
Page Load Tests Overall decrease ⚠️
vercel/next.js canary Kikobeats/next.js edge/cookies Change
/ failed reqs 0 0
/ total time (seconds) 3.941 3.935 -0.01
/ avg req/sec 634.37 635.34 +0.97
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.333 1.349 ⚠️ +0.02
/error-in-render avg req/sec 1875.27 1852.91 ⚠️ -22.36
Client Bundles (main, webpack)
vercel/next.js canary Kikobeats/next.js edge/cookies Change
925.HASH.js gzip 178 B 178 B
framework-HASH.js gzip 42.2 kB 42.2 kB
main-HASH.js gzip 29.2 kB 29.2 kB
webpack-HASH.js gzip 1.45 kB 1.45 kB
Overall change 73.1 kB 73.1 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary Kikobeats/next.js edge/cookies Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary Kikobeats/next.js edge/cookies Change
_app-HASH.js gzip 1.35 kB 1.35 kB
_error-HASH.js gzip 179 B 179 B
amp-HASH.js gzip 312 B 312 B
css-HASH.js gzip 324 B 324 B
dynamic-HASH.js gzip 2.71 kB 2.71 kB
head-HASH.js gzip 357 B 357 B
hooks-HASH.js gzip 921 B 921 B
image-HASH.js gzip 5.79 kB 5.79 kB
index-HASH.js gzip 261 B 261 B
link-HASH.js gzip 2.76 kB 2.76 kB
routerDirect..HASH.js gzip 322 B 322 B
script-HASH.js gzip 392 B 392 B
withRouter-HASH.js gzip 317 B 317 B
85e02e95b279..7e3.css gzip 107 B 107 B
Overall change 16.1 kB 16.1 kB
Client Build Manifests
vercel/next.js canary Kikobeats/next.js edge/cookies Change
_buildManifest.js gzip 456 B 456 B
Overall change 456 B 456 B
Rendered Page Sizes
vercel/next.js canary Kikobeats/next.js edge/cookies Change
index.html gzip 529 B 529 B
link.html gzip 543 B 543 B
withRouter.html gzip 526 B 526 B
Overall change 1.6 kB 1.6 kB
Commit: e010af7

@ijjk
Copy link
Member

ijjk commented Apr 27, 2022

Failing test suites

Commit: 9c8e9e5

yarn testheadless test/integration/middleware/core/test/index.test.js

  • Middleware base tests > dev mode > should allow to opt-out preflight caching
  • Middleware base tests > dev mode > should allow to opt-out preflight caching
  • Middleware base tests > production mode > should allow to opt-out preflight caching
  • Middleware base tests > production mode > should allow to opt-out preflight caching
Expand output

● Middleware base tests › dev mode › should allow to opt-out preflight caching

expect(received).toEqual(expected) // deep equality

Expected: "About Bypassed Page"
Received: "About Page"

  499 |     expect(await browser.eval('window.__SAME_PAGE')).toBe(true)
  500 |     const element = await browser.elementByCss('.title')
> 501 |     expect(await element.text()).toEqual('About Bypassed Page')
      |                                  ^
  502 |   })
  503 |
  504 |   it(`${locale} should not call middleware with shallow push`, async () => {

  at Object.<anonymous> (integration/middleware/core/test/index.test.js:501:34)

● Middleware base tests › dev mode › should allow to opt-out preflight caching

expect(received).toEqual(expected) // deep equality

Expected: "About Bypassed Page"
Received: "About Page"

  499 |     expect(await browser.eval('window.__SAME_PAGE')).toBe(true)
  500 |     const element = await browser.elementByCss('.title')
> 501 |     expect(await element.text()).toEqual('About Bypassed Page')
      |                                  ^
  502 |   })
  503 |
  504 |   it(`${locale} should not call middleware with shallow push`, async () => {

  at Object.<anonymous> (integration/middleware/core/test/index.test.js:501:34)

● Middleware base tests › production mode › should allow to opt-out preflight caching

expect(received).toEqual(expected) // deep equality

Expected: "About Bypassed Page"
Received: "About Page"

  499 |     expect(await browser.eval('window.__SAME_PAGE')).toBe(true)
  500 |     const element = await browser.elementByCss('.title')
> 501 |     expect(await element.text()).toEqual('About Bypassed Page')
      |                                  ^
  502 |   })
  503 |
  504 |   it(`${locale} should not call middleware with shallow push`, async () => {

  at Object.<anonymous> (integration/middleware/core/test/index.test.js:501:34)

● Middleware base tests › production mode › should allow to opt-out preflight caching

expect(received).toEqual(expected) // deep equality

Expected: "About Bypassed Page"
Received: "About Page"

  499 |     expect(await browser.eval('window.__SAME_PAGE')).toBe(true)
  500 |     const element = await browser.elementByCss('.title')
> 501 |     expect(await element.text()).toEqual('About Bypassed Page')
      |                                  ^
  502 |   })
  503 |
  504 |   it(`${locale} should not call middleware with shallow push`, async () => {

  at Object.<anonymous> (integration/middleware/core/test/index.test.js:501:34)

Read more about building and testing Next.js in contributing.md.

@Kikobeats Kikobeats force-pushed the edge/cookies branch 4 times, most recently from 8005914 to c2736a6 Compare April 27, 2022 12:18
@Kikobeats Kikobeats marked this pull request as ready for review April 27, 2022 12:23
timneutkens
timneutkens previously approved these changes May 3, 2022
Copy link
Member

@timneutkens timneutkens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice 👍

@kodiakhq kodiakhq bot merged commit b78c28f into vercel:canary May 9, 2022
@Kikobeats Kikobeats deleted the edge/cookies branch May 9, 2022 09:55
kodiakhq bot pushed a commit that referenced this pull request May 12, 2022
Follow up to #36478 

Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
kodiakhq bot pushed a commit that referenced this pull request May 19, 2022
Hello,

This is an iteration after first work at #36478.

What that PR missed is a way to just get a cookie value. Well, this PR adds two new things:

`cookies.get` returns the cookie value that could be `string | undefined`:

```js
const response = new NextResponse()
response.cookies.set('foo', 'bar', { path: '/test' })

const value = response.cookies.get('foo')
console.log(value) // => 'bar'
```

Additionally, if you want to know all the cookie details, you can use `cookies.getWithOptions`:

```js
const response = new NextResponse()

response.cookies.set('foo', 'bar', { path: '/test' })
const { value, options } response.cookies.getWithOptions('foo')

console.log(value) // => 'bar'
console.log(options) // => { Path: '/test' }
```
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Redesign the cookies API for NextResponse
4 participants