vike: Provide way of forcing a 404

Let’s say you have a page file that matches the current route, but upon fetching some data in the addPageContext hook, you determine that necessary data is unavailable and the user should be served with a 404 error page. At present, I believe the only option available to a user here is to halt rendering of the page and render an error would be to throw an error within the addPageContext hook. However, this will end up as a 500, not a 404. In this specific case, we are deliberately throwing an error that we would like to be cast as a 404.

I have a suggestion for how this could be achieved - export error classes that can be imported and used to throw specific error types. E.g.


import { ServerError, NotFoundError } from 'vite-plugin-ssr';

export async function addPageContext({ url }) {

  const criticalData = await fetchCriticalData(url);

  if (!criticalData) {
    throw new NotFoundError;
  }
  return {
    criticalData
  }
}


About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 15 (15 by maintainers)

Most upvoted comments

What is your concrete use case?

You can already hack something like this today.

export async function render() {
  if (!criticalData) {
    return {dataMissing: true};
  }
}
// server.js

const renderPage = createPageRender(/*...*/)

app.get('*', async (req, res, next) => {
  const result = await renderPage({ url: req.url })
  if (result.dataMissing) {
    const result = await renderPage({ url: '/doesNotExist' })
    res.status(result.statusCode).send(result.renderResult)
    return;
  }
})

return render404Error(Page)

Yea I was thinking about something like this as well.

Let me clarify:

// some.page.server.js

export async function addPageContext({ url }) {
  const criticalData = await fetchCriticalData(url);

  if (!criticalData) {
    return { dataIsMissing: true }
  }
  
  return { criticalData }
}
// _default.page.server.js

export function render({ dataIsMissing, Page }) {
  if( dataIsMissing ) {
    return { dataIsMissing: true };
  }
}
 // server.js

const { renderPage, renderErrorPage } = createPageRender(/*...*/)

app.get('*', async (req, res, next) => {
  const { url } = req.url
  let result = await renderPage({ url })
  if (result.dataMissing) {
    // We can add additional `pageContext` so that the error page knows that data was missing.
    result = await renderErrorPage({ url, is404: true, dataIsMissing: true })
  }
  res.status(result.statusCode).send(result.renderResult)
})

How would you event instruct the render to use the _error page template if you are explicitly running the render again?

We are not calling renderPage but renderErrorPage.

And note that, as usual, you can pass any initial pageContext to renderErrorPage. So that you can let the error page know that is has been called because some data were missing.

Also how would this work in a pre-rendering context?

While pre-rendering, you wouldn’t have the problem in the first place because you provide the list of URLs that are to be pre-rendered and you wouldn’t provide a URL that doesn’t exist (since the list of URLs will come from your source of truth).

How about:

// .page.server.js

export async function render() {
  if (!criticalData) {
    return {dataMissing: true};
  }
}
 // server.js

const { renderErrorPage } = createPageRender(/*...*/)

app.get('*', async (req, res, next) => {
  const { url } = req.url
  let result = await renderPage({ url })
  if (result.dataMissing) {
    result = await renderErrorPage({ url, is404: true })
  }
  res.status(result.statusCode).send(result.renderResult)
})

Concerns about this?

issue is mostly about providing clear guidance about a convention for error handling

I agree; I’ll add docs about this.