当前位置: 首页 > article >正文

使用Express.js和SQLite3构建简单TODO应用的后端API

使用Express.js和SQLite3构建简单TODO应用的后端API

        • 引言
        • 环境准备
        • 代码解析
          • 1. 导入必要的模块
          • 2. 创建Express应用实例
          • 3. 设置数据库连接
          • 4. 初始化数据库表
          • 5. 配置中间件
          • 6. 定义数据接口
          • 7. 定义路由
            • 7.1 获取所有TODO项
            • 7.2 创建TODO项
            • 7.3 更新TODO项
            • 7.4 删除TODO项
          • 8. 启动服务器
        • 优化建议
        • 总结

引言

在现代Web开发中,TODO列表应用是一个经典的示例,用于展示如何使用前端和后端技术构建一个简单的任务管理工具。本文将详细介绍如何使用Express.js框架和SQLite3数据库来构建一个TODO列表应用,并解释代码的各个部分,帮助读者理解其工作原理。

前端UI请参考,使用React和Material-UI构建TODO应用的前端UI

环境准备

在开始之前,请确保你已经安装了以下工具和库:

  1. Node.js:确保你已经安装了Node.js,可以从Node.js官网下载并安装。
  2. npm:Node.js的包管理工具,随Node.js一起安装。
  3. Express.js:一个轻量级的Node.js Web应用框架,可以通过npm安装。
  4. SQLite3:一个文件数据库,适用于小型应用,同样可以通过npm安装。
  5. TypeScript:可选,但推荐使用,以提升代码的可维护性和类型安全性。

安装所需的依赖:

npm install express cors sqlite3
代码解析

让我们逐步分析代码,理解每个部分的功能。

1. 导入必要的模块
import express from 'express';
import cors from 'cors';
import sqlite3 from 'sqlite3';
import path from 'path';
  • Express:用于创建Web服务器。
  • CORS:处理跨域请求,允许前端应用从不同的域名访问后端API。
  • SQLite3:用于与SQLite数据库进行交互。
  • Path:处理文件路径,确保在不同操作系统下路径正确。
2. 创建Express应用实例
const app = express();

这行代码创建了一个Express应用实例,后续的所有中间件和路由都将注册到这个实例上。

3. 设置数据库连接
const db = new sqlite3.Database(path.join(__dirname, '../database/db.sqlite'));

这里创建了一个SQLite3数据库连接,数据库文件位于../database/db.sqlitepath.join用于确保路径在不同操作系统下都能正确解析。

4. 初始化数据库表
db.serialize(() => {
    db.run(`
        CREATE TABLE IF NOT EXISTS todos
        (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            description TEXT,
            completed BOOLEAN DEFAULT 0,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
    `);
});

这段代码在数据库中创建了一个名为todos的表,如果该表不存在的话。表结构如下:

  • id:整数类型,主键,自增。
  • title:文本类型,非空。
  • description:文本类型,可选。
  • completed:布尔类型,默认值为0(即false)。
  • created_at:日期时间类型,默认值为当前时间戳。
5. 配置中间件
app.use(cors());
app.use(express.json());
  • CORS中间件:允许来自不同域名的请求,防止跨域问题。
  • JSON中间件:解析请求体中的JSON数据,使得req.body可以访问到客户端发送的数据。
6. 定义数据接口
interface Todo {
    id?: number;
    title: string;
    description?: string;
    completed?: boolean;
    created_at?: string;
}

这是一个TypeScript接口,定义了TODO项的结构。iddescriptioncompletedcreated_at是可选的,而title是必填的。

7. 定义路由
7.1 获取所有TODO项
app.get('/api/todos', (req: express.Request, res: express.Response) => {
    const search = (req.query.q as string) || '';
    db.all<Todo>(
        `SELECT *
         FROM todos
         WHERE title ILIKE ? 
            OR description ILIKE ?
         ORDER BY created_at DESC`,
        [`%${search}%`, `%${search}%`],
        (err, rows) => {
            if (err) return res.status(500).json({ error: err.message });
            res.json(rows);
        }
    );
});
  • 功能:获取所有TODO项,支持通过q参数进行模糊搜索。
  • 查询参数q用于模糊搜索标题或描述。
  • 数据库操作:使用ILIKE进行不区分大小写的匹配,按创建时间降序排列。
  • 错误处理:如果数据库操作失败,返回500状态码和错误信息。
7.2 创建TODO项
app.post('/api/todos', (req: express.Request, res: express.Response) => {
    const { title, description } = req.body as Todo;
    if (!title) {
        return res.status(400).json({ error: 'Title is required' });
    }

    db.run(
        'INSERT INTO todos (title, description) VALUES (?, ?)',
        [title, description],
        function (err) {
            if (err) return res.status(500).json({ error: err.message });
            db.get<Todo>(
                'SELECT * FROM todos WHERE id = ?',
                [this.lastID],
                (err, row) => {
                    if (err) return res.status(500).json({ error: err.message });
                    res.status(201).json(row);
                }
            );
        }
    );
});
  • 功能:创建一个新的TODO项。
  • 请求体:必须包含titledescription可选。
  • 数据库操作:插入新记录,然后查询新插入的记录并返回。
  • 错误处理:检查title是否为空,返回400错误;数据库操作失败返回500错误。
7.3 更新TODO项
app.put('/api/todos/:id', (req: express.Request, res: express.Response) => {
    const { id } = req.params;
    const { title, description, completed } = req.body as Todo;

    db.run(
        `UPDATE todos
         SET title       = ?,
             description = ?,
             completed   = ?
         WHERE id = ?`,
        [title, description, completed ? 1 : 0, id],
        function (err) {
            if (err) return res.status(500).json({ error: err.message });
            if (this.changes === 0) {
                return res.status(404).json({ error: 'Todo not found' });
            }
            db.get<Todo>(
                'SELECT * FROM todos WHERE id = ?',
                [id],
                (err, row) => {
                    if (err) return res.status(500).json({ error: err.message });
                    res.json(row);
                }
            );
        }
    );
});
  • 功能:更新指定ID的TODO项。
  • 请求体:包含titledescriptioncompleted
  • 数据库操作:更新记录,completed字段转换为10
  • 错误处理:如果没有记录被更新,返回404错误;数据库操作失败返回500错误。
7.4 删除TODO项
app.delete('/api/todos/:id', (req: express.Request, res: express.Response) => {
    const { id } = req.params;
    db.run(
        'DELETE FROM todos WHERE id = ?',
        [id],
        function (err) {
            if (err) return res.status(500).json({ error: err.message });
            if (this.changes === 0) {
                return res.status(404).json({ error: 'Todo not found' });
            }
            res.status(204).send();
        }
    );
});
  • 功能:删除指定ID的TODO项。
  • 数据库操作:删除记录。
  • 错误处理:如果没有记录被删除,返回404错误;数据库操作失败返回500错误。
8. 启动服务器
const PORT = 3002;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

服务器监听在3002端口,启动后在控制台输出提示信息。

优化建议

尽管这段代码已经可以正常工作,但为了提升应用的性能和可维护性,可以考虑以下优化:

  1. 使用连接池: SQLite3支持连接池,可以提高数据库操作的效率,特别是在高并发情况下。
  2. 添加输入验证:使用中间件如express-validator对请求体进行验证,确保数据的合法性和完整性。
  3. 添加分页功能:在获取TODO列表时,添加分页功能,限制每页返回的记录数,提高性能。
  4. 使用ORM工具:如SequelizeKnex.js,简化数据库操作,提高代码的可读性和可维护性。
  5. 添加日志记录:使用winstonmorgan记录请求和错误信息,便于调试和监控。
  6. 添加速率限制:防止恶意攻击,限制同一IP地址的请求频率。
总结

通过本文,我们详细解析了一个使用Express.js和SQLite3构建的TODO列表应用。从数据库的初始化、路由的定义到错误处理,每个部分都进行了详细的解释。希望这篇文章能够帮助读者理解如何使用这些技术构建一个简单的Web应用,并为进一步的学习和开发打下基础。


http://www.kler.cn/a/531017.html

相关文章:

  • 万字长文深入浅出负载均衡器
  • LeetCode 2909. 元素和最小的山形三元组 II
  • 62.病毒在封闭空间中的传播时间|Marscode AI刷题
  • 快速提升网站收录:利用网站用户反馈机制
  • python学opencv|读取图像(五十三)原理探索:使用cv.matchTemplate()函数实现最佳图像匹配
  • MapReduce简单应用(一)——WordCount
  • cf div3 998 E(并查集)
  • 几种用户鉴权的方式对比
  • Kamailio、MySQL、Redis、Gin后端、Vue.js前端等基于容器化部署
  • 讲清逻辑回归算法,剖析其作为广义线性模型的原因
  • volatile变量需要减少读取次数吗
  • 49【服务器介绍】
  • 常见的 Vue.js 组件库:Element Plus, Vuetify, Quasar
  • NeuralCF 模型:神经网络协同过滤模型
  • docker pull Error response from daemon问题
  • [HOT 100] 2824. 统计和小于目标的下标对数目
  • FreeRTOS从入门到精通 第十九章(内存管理)
  • 【大数据技术】教程05:本机DataGrip远程连接虚拟机MySQL/Hive
  • 《tcp/ip协议详解》,tcp/ip协议详解
  • Vue-data数据对象
  • 列表的简介
  • 新月军事战略分析系统使用手册
  • Linux中的基本指令(二)
  • Deep Crossing:深度交叉网络在推荐系统中的应用
  • 洛谷 P8724 [蓝桥杯 2020 省 AB3] 限高杆
  • 深入理解Java虚拟线程的同步编程模型