Skip to content

[RFC] server(less) middleware #7208

Closed
@timneutkens

Description

@timneutkens

Feature request

Currently there are a lot of users that create a custom server to do middleware behavior, for example server-side parsing of request bodies, cookies etc. Currently Next.js provides no way of handling this without a custom server. Meaning it can't be done in serverless / other environments.

Describe the solution you'd like

Ideally users would define middleware as a top level export. Meaning that pages can define middleware requirements:

// PageContext being the same values as `getInitialProps`'s context
export async function middleware({req, res}: PageContext) {
  // do something with `req` / `res`
}

function IndexPage() {
  return <h1>Hello world</h1>
}

export default IndexPage

However this also introduces the complexity of having to code-split / tree shake away user imports that are server-only.

So for the initial implementation (and incrementally working towards the implementation above) I'd start with supporting middleware in pages_document.js which is always server-side rendered so it makes sense to have it there.

One thing that was brought up is "why can't this just be done in getInitialProps of _document".

The reason that we need a new method is that the lifecycle of calling getInitialProps looks like this:

  • pages/_app.js getInitialProps is called,
  • pages/_app.js getInitialProps calls the page's getInitialProps
  • pages/_document.js getInitialProps is called
  • pages/_document.js getInitialProps calls renderPage
  • renderPage calls React's renderToString and returns the html, head tags and styles.
  • renderToStaticMarkup is called to render the _document shell
  • request is ended using send-html.ts, which adds etag etc.

Generally when using middleware it has to be called before getInitialProps because you could be parsing something needed in getInitialProps

So the middleware has to be called earlier.

Meaning the lifecycle would look like:

  • pages/_document.js middleware is called
  • pages/_app.js getInitialProps is called,
  • pages/_app.js getInitialProps calls the page's getInitialProps
  • pages/_document.js getInitialProps is called
  • pages/_document.js getInitialProps calls renderPage
  • renderPage calls React's renderToString and returns the html, head tags and styles.
  • renderToStaticMarkup is called to render the _document shell
  • request is ended using send-html.ts, which adds etag etc.

So for the initial implementation we'd want something like:

// pages/_document.js

// PageContext being the same values as `getInitialProps`'s context
export async function middleware({req, res}: PageContext) {
  // do something with `req` / `res`
}

// rest of _document.js

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions