如何将多页面Web应用(MPA)连接至GraphQL?后端渲染方案问询
Hey there! Let's break down how to hook up your new MPA CMS to your existing Express + GraphQL backend, including how to fetch data server-side and render pages for end users. Here's a step-by-step approach based on practical experience:
1. Fetching GraphQL Data from Your Express Backend
First, you need a way for your Express server to query your GraphQL endpoint. You have two solid options here:
Option A: Use Apollo Client (Node.js Version)
Apollo Client isn't just for frontend—it works great on Node.js too, and gives you built-in features like caching (optimized for SSR) and structured error handling.
Install dependencies:
npm install @apollo/client graphqlInitialize Apollo Client in Express:
Create a reusable client instance (you can put this in a separate file likeapollo-client.js):const { ApolloClient, InMemoryCache, createHttpLink } = require('@apollo/client'); const { setContext } = require('@apollo/client/link/context'); // Point to your existing GraphQL endpoint const httpLink = createHttpLink({ uri: 'http://localhost:4000/graphql', // Update this to your actual GraphQL URL }); // Optional: Add auth if your GraphQL endpoint requires it (e.g., JWT from cookies) const authLink = setContext((_, { headers }) => { // Grab auth token from incoming request cookies (adjust based on your auth setup) const token = req.cookies?.authToken; return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '', }, }; }); // Initialize client with SSR mode enabled (avoids client-side caching conflicts) const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), ssrMode: true, }); module.exports = client;Use the client in your Express routes:
const client = require('./apollo-client'); const { gql } = require('@apollo/client'); app.get('/article/:id', async (req, res) => { try { // Query your GraphQL API for the article data const { data } = await client.query({ query: gql` query GetArticle($id: ID!) { article(id: $id) { title content author { name } publishDate } } `, variables: { id: req.params.id }, }); // If no article found, send 404 if (!data.article) { return res.status(404).render('404'); } // Pass data to your template renderer (we'll cover this next) res.render('article', { article: data.article }); } catch (err) { console.error('GraphQL fetch error:', err); res.status(500).render('500'); } });
Option B: Raw Fetch API (No Extra Dependencies)
If you prefer to keep things lightweight, you can directly send POST requests to your GraphQL endpoint using Node's native fetch (or node-fetch if you're on an older Node version):
app.get('/article/:id', async (req, res) => { try { const response = await fetch('http://localhost:4000/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json', // Add auth headers here if needed (e.g., from cookies) ...(req.cookies.authToken && { authorization: `Bearer ${req.cookies.authToken}` }), }, body: JSON.stringify({ query: ` query GetArticle($id: ID!) { article(id: $id) { title content author { name } publishDate } } `, variables: { id: req.params.id }, }), }); const result = await response.json(); if (result.errors) { throw new Error(result.errors[0].message); } if (!result.data.article) { return res.status(404).render('404'); } res.render('article', { article: result.data.article }); } catch (err) { console.error('Fetch error:', err); res.status(500).render('500'); } });
2. Rendering MPA Pages with a Template Engine
For your MPA, use a server-side template engine to generate HTML from fetched data. We'll use EJS as an example (Pug or Handlebars work similarly):
Install EJS:
npm install ejsConfigure Express to use EJS:
// Set EJS as the view engine app.set('view engine', 'ejs'); // Define where your template files live (create this folder if it doesn't exist) app.set('views', './views');Create a template file (e.g.,
views/article.ejs):<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title><%= article.title %></title> <link rel="stylesheet" href="/css/styles.css"> </head> <body> <header> <h1>My CMS</h1> </header> <main> <article> <h2><%= article.title %></h2> <p class="meta">By <%= article.author.name %> | <%= article.publishDate %></p> <div class="content"><%- article.content %></div> </article> </main> <footer> <p>© 2024 My CMS</p> </footer> </body> </html>Add static file serving (for CSS, JS, images):
app.use(express.static('public'));Put your static assets (like
css/styles.css) in apublicfolder at your project root.
3. Key Optimizations & Considerations
- Error Handling: Always catch GraphQL query errors and render user-friendly error pages (404 for missing content, 500 for server issues).
- Caching: For frequently accessed content, add server-side caching (e.g., with Redis) to reduce repeated GraphQL queries and boost performance.
- Auth & Permissions: If some content is restricted, ensure your Express server passes the correct auth tokens to the GraphQL endpoint (as shown in the Apollo Client example with cookies).
- Static Site Generation (SSG): If your content doesn't change often, pre-render pages at build time using tools like Next.js (with SSG features) — this is optional if you need dynamic server-side rendering.
内容的提问来源于stack exchange,提问作者Kucl Stha

