如何在Netlify部署Node.js Express项目?解决404错误
我是前端开发者,后端开发新手,用Node.js + Express搭建后端、React搭建前端开发了一款URL短链工具。本地运行完全正常,但部署到Netlify后出现404错误,无法正常使用。环境变量配置无误,但访问后端URL无任何响应,推测问题出在Netlify的构建配置上,求正确的构建配置方案及问题解决办法。
后端代码
const express = require('express'); const shortid = require('shortid'); const cors = require('cors'); const { resolve } = require('url'); const app = express(); const PORT = 5000; require('dotenv').config(); const urlDatabase = {}; const corsOptions = { origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, optionsSuccessStatus: 204, }; app.use(cors(corsOptions)); app.use(express.json()); app.post('/shorten', (req, res) => { const { originalUrl } = req.body; const shortCode = shortid.generate(); const shortUrl = `${process.env.BACKEND_URL}/${shortCode}`; urlDatabase[shortCode] = originalUrl; res.json({ shortUrl }); }); app.get('/:shortCode', (req, res) => { const { shortCode } = req.params; const originalUrl = urlDatabase[shortCode]; if (originalUrl) { res.redirect(originalUrl); } else { res.status(404).json({ error: 'URL not found' }); } }); app.listen(PORT, () => { console.log(`Server is running on '${process.env.BACKEND_URL}:${PORT}'`); });
前端代码
import React, { useState } from 'react'; import axios from 'axios'; import { Form, Button, Alert } from 'react-bootstrap'; import './App.css'; const App = () => { const [originalUrl, setOriginalUrl] = useState(''); const [shortenedUrl, setShortenedUrl] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const shortenUrl = async () => { try { if (!originalUrl) { setErrorMessage('Por favor, digite uma URL válida.'); setShortenedUrl(''); } else { console.log('Backend URL:', process.env.REACT_APP_BACKEND_URL); const response = await axios.post(`${process.env.REACT_APP_BACKEND_URL}/shorten`, { originalUrl, }); setShortenedUrl(response.data.shortUrl); setErrorMessage(''); } } catch (error) { console.error('Error shortening URL:', error); setErrorMessage('Ocorreu um erro ao encurtar a URL. Tente novamente.'); setShortenedUrl(''); } }; return ( <div className="App"> <div className="container"> <h1>Encurtador de Link</h1> </div> <Form className='form'> <Form.Group controlId="formOriginalUrl" className='form-url-original'> <Form.Label className='text-url'>Insira o Link Original</Form.Label> <Form.Control className='input' type="url" placeholder="Digite o Link original" value={originalUrl} onChange={(e) => setOriginalUrl(e.target.value)} /> </Form.Group> <Button variant="primary" className='btn-url-original' onClick={shortenUrl}> Encurtar Link </Button> </Form> {errorMessage && <Alert variant="danger" className="mt-3 new-url">{errorMessage}</Alert>} {shortenedUrl && !errorMessage && ( <div className="mt-3 new-url"> <p>Seu Novo Link</p> <a href={shortenedUrl} target="_blank" rel="noopener noreferrer"> {shortenedUrl} </a> </div> )} </div> ); }; export default App;
核心问题
Netlify默认是静态站点托管平台,不会自动运行Express后端服务。本地环境中前后端分开启动,但Netlify需要将后端逻辑转为**Netlify Functions(无服务器函数)**部署,同时配置重写规则转发API请求。
步骤1:重构后端为Netlify Functions
Netlify Functions要求函数放在项目根目录的netlify/functions文件夹下,每个函数对应一个接口:
项目结构调整
你的项目/ ├── netlify/ │ └── functions/ │ ├── shorten.js # 处理URL缩短请求 │ └── redirect.js # 处理短链跳转 └── src/ # React前端代码
编写shorten.js函数
const shortid = require('shortid'); // 注意:Netlify Functions是无状态的,内存存储会在函数重启后丢失,建议后续替换为外部数据库(如Supabase/Redis) const urlDatabase = {}; exports.handler = async (event, context) => { // 处理CORS预检请求 if (event.httpMethod === 'OPTIONS') { return { statusCode: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }, }; } if (event.httpMethod === 'POST') { try { const { originalUrl } = JSON.parse(event.body); const shortCode = shortid.generate(); // 使用Netlify站点域名作为短链前缀 const shortUrl = `${process.env.URL}/${shortCode}`; urlDatabase[shortCode] = originalUrl; return { statusCode: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json', }, body: JSON.stringify({ shortUrl }), }; } catch (err) { return { statusCode: 500, headers: { 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify({ error: '处理请求失败' }), }; } } return { statusCode: 405, headers: { 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify({ error: '不支持的请求方法' }), }; };
编写redirect.js函数
const urlDatabase = {}; // 同样需要替换为外部数据库 exports.handler = async (event, context) => { const { shortCode } = event.queryStringParameters; const originalUrl = urlDatabase[shortCode]; if (originalUrl) { return { statusCode: 302, headers: { Location: originalUrl, 'Access-Control-Allow-Origin': '*', }, }; } else { return { statusCode: 404, headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json', }, body: JSON.stringify({ error: 'URL不存在' }), }; } };
步骤2:配置Netlify重写规则
在项目根目录创建netlify.toml文件,将前端API请求转发到对应函数:
[build] command = "npm run build" publish = "build" [[redirects]] from = "/shorten" to = "/.netlify/functions/shorten" status = 200 [[redirects]] from = "/:shortCode" to = "/.netlify/functions/redirect?shortCode=:shortCode" status = 302
步骤3:调整前端请求地址
前端无需再配置独立的后端URL,直接使用Netlify站点域名即可,可通过window.location.origin动态获取:
const response = await axios.post(`${window.location.origin}/shorten`, { originalUrl, });
步骤4:解决无状态存储问题
Netlify Functions每次调用后内存会被清空,urlDatabase的数据会丢失,必须将短链数据存储到外部数据库,比如:
- Supabase(免费额度满足小型项目需求)
- Redis
- FaunaDB
以Supabase为例,需安装@supabase/supabase-js,并在函数中替换内存存储逻辑为数据库的读写操作。
步骤5:部署到Netlify
- 将代码推送到GitHub/GitLab仓库
- 在Netlify后台关联该仓库,配置所需环境变量(如数据库密钥)
- 触发自动构建,Netlify会自动识别
netlify.toml和Functions文件夹完成部署
本地测试
安装Netlify CLI:npm install -g netlify-cli,然后运行netlify dev即可在本地模拟Netlify环境,测试函数和前端的交互。
内容的提问来源于stack exchange,提问作者Renan Alexandre Berton

