We are looking for a way to perform error handling of SSR without compromising response performance on the SPA.

Asked 2 years ago, Updated 2 years ago, 79 views

I have also asked StackOverflow headquarters questions, but I have not been able to answer them, so please let me ask you a question.
If there is a useful answer, I will reflect it to both of them.

  • SPA=Single Page Application
  • SSR=Server Side Rendering

Suppose you implement SSR with express and React and an error is thrown during rendering.
We will consider returning the following data when we catch the error.

  • Returns status code 500
  • Add a custom header to determine the type of error

Furthermore, because of the complexity of this application, we want to stream the data that we pass to clients to improve rendering performance, starting with what we can send.Therefore, the HTTP response body may arrive before the SSR fails.

For example:

  • <!DOCTYPE html>
  • Fixed <head> Tag Contents

To reproduce these requests, we provide two implementation examples using renderToStaticNodeStream and renderToStaticMarkup.

GitHub: https://github.com/Himenon/react-ssr-error-handle

import* as React from "react";
import express from "express";
import {
  renderToStaticNodeStream,
  renderToStaticMarkup,
} from "react-dom/server";

const SERVER_PORT = 9000;

const server = express();

const LargeApplication=()=>{
  const somethingError=()=>{
    console.log(`Access window object in nodejs:${window.location.href}`);
  };
  somethingError();
  return(
    <html lang="en">
      <head>
        <title>React SSR Streaming Error Handle</title>
      </head>
      <body>
        <h1>Hello world!</h1>
      </body>
    </html>
  );
};

server.get("/ssr-error",(req:express.Request,res:express.Response)=>{
  res.status (500);
  res.send("<p>SSRERROR</p>");
});

server.get("/sample1",(req:express.Request,res:express.Response)=>{
  conststream=renderToStaticNodeStream(<LargeApplication/>);
  res.type("html");
  res.write("<!DOCTYPE html>");
  stream.pipe(res, {end:true});
  stream.on("error",(error)=>{
    // res.redirect("/ssr-error"); // ERR_HTTP_HEADERS_SENT appears when redirecting during error handling
    res.status (500);
    res.setHeader(
      "Custom-Error-Code",
      "REACT: RENDER_TO_STATIC_NODE_STREAM_ERROR"
    );
    res.write(`<pre><code>${error.stack}</code></pre>`);
    res.end();
  });
});

server.get("/sample2",(req:express.Request,res:express.Response)=>{
  try{
    res.type("html");
    res.write("<!DOCTYPE html>";//can be immediate responded to
    consthtml=renderToStaticMarkup(<LargeApplication/>);
    /* <----- I know that description it's here't cause any problems.*/
    // res.type("html");
    // res.write("<!DOCTYPE html>");
    /* ---->*/
    res.write(html);
  } catch(error){
    res.status (500);
    res.setHeader(
      "Custom-Error-Code",
      "REACT: RENDER_TO_STATIC_NODE_STREAM_ERROR"
    );
    res.send(`<pre><code>${error.stack}</code></pre>`);
  }
});

server.listen(SERVER_PORT,() = > {
  console.log(`Serve start: http://localhost:${SERVER_PORT}}`);
});

This server has the following URL:

Accessing /sample1 results in the following error that causes the server to stop:

Error [ERR_HTTP_HEADERS_SENT]—Cannot set heads after they are sent to the client
    at ServerResponse.setHeader(_http_outgoing.js:485:11) react-ssr-error-handle/src/server.tsx:33:9)
    at ReactMarkupReadableStream.emit (events.js:210:5)
    at ReactMarkupReadableStream.EventEmit.emit (domain.js:475:20)
    enterErrorNT (internal/streams/destroy.js:92:8)
    enterErrorAndCloseNT (internal/streams/destroy.js:60:3)
    at processTicksAndRejections (internal/process/task_queues.js:80:21)
error Command failed with exit code 1.

The reason is clear, because the res.setHeader that you send based on the error you catch is later than the res.write("<!DOCTYPE html>"), so the response header is later than the response body.

/sample2 also throws an error for the same reason and terminates the server.

The solution is simple.

The realistic implementation is 1, and we are currently adopting this implementation method.
3 is concerned that Google searches will index the state at the time of error (not verified).

The sample code shown here is a relatively small application.Therefore, you can aim for a complete implementation that does not throw errors during SSR.In practice, however, applications rely on many libraries and have a wide range of conditional branches, making it difficult to achieve a full implementation.Furthermore, since front-end applications require faster rendering, developers want to implement them to return results to users faster.For example, shortening the TTFB.

Do you have any other suggestions for error handling?

Even if you split the components and SSR as much as you need for the first display, you will also encounter problems such as this question if you throw an error during SSR.Therefore, we removed the description of the application's large and small problems from the sentence.

node.js reactjs

2022-09-30 19:27

1 Answers

If it were me,
I think it would be difficult to bring such a big page at once even if it is called SSR in SPA.
Wouldn't it be realistic to make server inquiries using Ajax or something like that every time you need only the necessary parts?

GoogleMap, for example, consists of a single page, but it does not bring all the pages, but rather it does not load large pages because of limited screen size, so it reads and renders them as much as it needs.

If you want to contact the server each time,
If an error occurs, you can take care of it every time.

Note:
The size of the application disappeared from the question, so I answered more.
If you have an individual SSR error, or if you have to stop the page with a fatal number of 500, you should prepare a page for 500 output and redirect it to it.


2022-09-30 19:27

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.