使用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 todosWHERE 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 todosSET 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.mzph.cn/diannao/69259.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows本地部署DeepSeek-R1大模型并使用web界面远程交互

文章目录 前言1. 安装Ollama2. 安装DeepSeek-r1模型3. 安装图形化界面3.1 Windows系统安装Docker3.2 Docker部署Open WebUI3.3 添加Deepseek模型 4. 安装内网穿透工具5. 配置固定公网地址 前言 最近爆火的国产AI大模型Deepseek详细大家都不陌生&#xff0c;不过除了在手机上安…

低代码开发与传统开发:未来的技术路线选择

在科技飞速发展的当下&#xff0c;软件开发技术日新月异&#xff0c;低代码开发与传统开发作为两种重要的开发模式&#xff0c;正站在未来技术路线选择的十字路口&#xff0c;引发了众多企业和开发者的关注。它们各自有着独特的优势和适用场景&#xff0c;究竟该如何抉择&#…

二、0-1搭建springboot+vue3前后端分离-登录页面

项目仓库地址&#xff1a;zgw-admin: 从0-1搭建一个springbootvue3的项目&#xff0c;这是源码 本次主要是为了&#xff1a; a.写登录页面 b.element plus组件是否能正常使用 c.页面调用ts是否正常&#xff0c;无参和有参的函数 首页的图片&#xff1a; 页面效果 1、引入…

《翻转组件库之发布》

背景 继《翻转组件库之打包》_杨晓风-linda的博客-CSDN博客之后&#xff0c;组件库已经可以正常构建&#xff0c;那如何像elementUI等组件库那样&#xff0c;用npm安装&#xff0c;按照既定的用法使用即可呢&#xff1f;本篇便为你揭晓 资料相关 1、npm官方文档&#xff1a;…

Spring Task之Cron表达式

&#x1f31f; Spring Task高能预警&#xff1a;你以为的Cron表达式可能都是错的&#xff01;【附实战避坑指南】 开篇暴击&#xff1a;为什么你的定时任务总在凌晨3点翻车&#xff1f; “明明设置了0 0 2 * * ?&#xff0c;为什么任务每天凌晨3点执行&#xff1f;” —— 来…

web-JSON Web Token-CTFHub

前言 在众多的CTF平台当中&#xff0c;作者认为CTFHub对于初学者来说&#xff0c;是入门平台的不二之选。CTFHub通过自己独特的技能树模块&#xff0c;可以帮助初学者来快速入门。具体请看官方介绍&#xff1a;CTFHub。 作者更新了CTFHub系列&#xff0c;希望小伙伴们多多支持…

如何在 Kafka 中实现自定义分区器

今天我来给大家分享一下如何在 Kafka 中实现一个自定义分区器。Kafka 是一个分布式流处理平台&#xff0c;能够高效地处理海量数据。默认情况下&#xff0c;Kafka 使用键的哈希值来决定消息应该发送到哪个分区&#xff0c;但是有时我们需要根据特定的业务逻辑来定制分区策略。这…

【FPGA】 MIPS 12条整数指令【2】

目录 实现slt 仿真 代码 完整代码 ID.v DataMem.v define.v EX.v IF.v InstMem.v MEM.v MIPS.v RegFile.v Soc.v soc_tb.v 实现slt 仿真 ori r1,r0,1100h ori r2,r0,0020h ori r3,r0,ff00h ori r4,r0,ffffh addi r5,r0,ffff slt r6,r5,r4 slt r6,r4,r…

MySQL 进阶专题:索引(索引原理/操作/优缺点/B+树)

在数据库的秋招面试中&#xff0c;索引&#xff08;Index&#xff09;是一个经典且高频的题目。索引的作用类似于书中的目录&#x1f4d6;&#xff0c;它能够显著加快数据库查询的速度。本文将深入探讨索引的概念、作用、优缺点以及背后的数据结构&#xff0c;帮助你从原理到应…

nginx目录结构和配置文件

nginx目录结构 [rootlocalhost ~]# tree /usr/local/nginx /usr/local/nginx ├── client_body_temp # POST 大文件暂存目录 ├── conf # Nginx所有配置文件的目录 │ ├── fastcgi.conf # fastcgi相关参…

vue-router 有哪几种导航钩子?

在 Vue Router 中,导航钩子(Navigation Guards)用于控制路由的进入和离开,可以在路由变化的不同阶段执行逻辑。Vue Router 提供了多种类型的导航钩子,主要包括以下几种: 一、全局导航钩子 全局导航钩子在路由实例上定义,适用于所有路由的导航。 beforeEach在每次路由切…

信息学奥赛一本通 2101:【23CSPJ普及组】旅游巴士(bus) | 洛谷 P9751 [CSP-J 2023] 旅游巴士

【题目链接】 ybt 2101&#xff1a;【23CSPJ普及组】旅游巴士(bus) 洛谷 P9751 [CSP-J 2023] 旅游巴士 【题目考点】 1. 图论&#xff1a;求最短路Dijkstra, SPFA 2. 动态规划 3. 二分答案 4. 图论&#xff1a;广搜BFS 【解题思路】 解法1&#xff1a;Dijkstra堆优化 …

C基础寒假练习(6)

一、终端输入行数&#xff0c;打印倒金字塔 #include <stdio.h> int main() {int rows;printf("请输入倒金字塔的行数: ");scanf("%d", &rows);for (int i rows; i > 0; i--) {// 打印空格for (int j 0; j < rows - i; j) {printf(&qu…

vim modeline

1. 什么是 Vim 模型行&#xff08;modeline&#xff09;&#xff1f; Vim 模型行是嵌入在文件中的特殊注释行&#xff0c;用于告诉 Vim 编辑器如何配置编辑选项。它的语法格式如下&#xff1a; # vim: 选项1值1:选项2值2:...它以 # vim: 开头&#xff08;# 是注释符&#xff…

【C# 】图像资源的使用

在C#中&#xff0c;图像资源的使用方式方法主要依赖于你所使用的框架和库。以下是几种常见的使用图像资源的方法&#xff1a; Windows Forms 直接加载图像&#xff1a; 使用System.Drawing.Image.FromFile()方法可以直接从文件系统加载图像。 Image image Image.FromFile(&qu…

OpenGL学习笔记(六):Transformations 变换(变换矩阵、坐标系统、GLM库应用)

文章目录 向量变换使用GLM变换&#xff08;缩放、旋转、位移&#xff09;将变换矩阵传递给着色器坐标系统与MVP矩阵三维变换绘制3D立方体 & 深度测试&#xff08;Z-buffer&#xff09;练习1——更多立方体 现在我们已经知道了如何创建一个物体、着色、加入纹理。但它们都还…

java后端开发面试常问

面试常问问题 1 spring相关 &#xff08;1&#xff09;Transactional失效的场景 <1> Transactional注解默认只会回滚运行时异常&#xff08;RuntimeException&#xff09;&#xff0c;如果方法中抛出了其他异常&#xff0c;则事务不会回滚&#xff08;数据库数据仍然插…

使用conda创建自己的python虚拟环境,与其他python版本独立区分

使用 Conda 创建和使用自己的运行环境非常简单&#xff0c;以下是详细步骤&#xff1a; 1. 安装 Anaconda 或 Miniconda 如果你尚未安装 Anaconda 或 Miniconda&#xff0c;可以访问 Anaconda 官网 或 Miniconda 官网 下载并安装。 2. 创建新的 Conda 虚拟环境 创建虚拟环境…

OSPF基础(1):工作过程、状态机、更新

OSPF基础 1、技术背景&#xff08;与RIP密不可分&#xff0c;因为RIP中存在的问题&#xff09; RIP中存在最大跳数为15的限制&#xff0c;不能适应大规模组网周期性发送全部路由信息&#xff0c;占用大量的带宽资源以路由收敛速度慢以跳数作为度量值存在路由环路可能性每隔30秒…

python爬虫--简单登录

1&#xff0c;使用flask框架搭建一个简易网站 后端代码app.py from flask import Flask, render_template, request, redirect, url_for, sessionapp Flask(__name__) app.secret_key 123456789 # 用于加密会话数据# 模拟用户数据库 users {user1: {password: password1}…