我从零开始构建了一个 Full-Stack Invoice App。以下是完整拆解

发布: (2026年5月2日 GMT+8 05:47)
3 分钟阅读
原文: Dev.to

Source: Dev.to

大多数发票工具要么太贵,要么太复杂。我在一周内自行构建了一个,已上线部署,并记录了每个技术决策和经验教训。

Live Demo & Source Code

Tech Stack

  • Frontend: React
  • Backend: Node.js with Express
  • Database: PostgreSQL
  • Deployment: Frontend on Vercel, backend on Render

Authentication Middleware

每个受保护的路由在到达控制器之前都会经过同一个中间件。

// auth.js
const auth = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'No token provided' });
  }
  const token = authHeader.split(' ')[1];
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ error: 'Invalid or expired token' });
  }
};

简洁、可复用,并用一个函数保护所有路由。

Automatic Database Migrations

服务器在启动时会自动创建表,因此克隆仓库的任何人都能在几秒钟内获得可用的数据库。

// server.js
migrate.createTables().then(() => {
  app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
  });
});

Global Axios Interceptor

无需手动为每个请求附加 JWT 令牌,拦截器会全局添加它。

// api.js
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

写一次,免去后顾之忧。

Deployment Gotchas

  1. Render SSL for PostgreSQL – 在生产环境的 pg Pool 配置中添加 rejectUnauthorized: false,否则连接会失败。
  2. CORS configuration – 明确列出你的 Vercel 域名;通配符在使用凭证时无效。
  3. JWT expiry values – 环境变量必须是字符串。如果值为 undefinedjwt.sign 会抛出静默错误。请提供默认值。

这些问题花了我两小时;现在它们对你来说不花一分钱。

Dashboard Overview

仪表盘显示:

  • 发票总数
  • 客户总数
  • 已收收入
  • 未结余额

所有数据均通过实时数据库查询在每次加载时计算——没有虚假数据或硬编码的数字。

Additional Features

  • 发票 PDF 导出
  • 通过 Nodemailer 发送邮件
  • 循环发票
  • 支付网关集成

Conclusion

如果此拆解对你有帮助,请考虑在 GitHub 上给仓库加星并留下评论。

Tags: javascript, node, react, webdev

0 浏览
Back to Blog

相关文章

阅读更多 »

模型越智能,节省越多。

神话:更智能的模型会让插件变得多余。自从 WOZCODE 推出以来,许多 Claude Code 高级用户低声说插件的优势将会消失。