
无论你是做一个MVP,还是完整的 Web 应用,存储和读取数据都至关重要。SQLite 是一款可移植、零配置的轻型数据库,能让你快速上手。
1. 为什么要在 Next.js 中使用 SQLite?
- Next.js 是构建服务端器 React 框架,自动处理路由、SSR、代码分割等。
- SQLite 是一款文件型 SQL 数据库引擎,轻量、快速且零配置。
Next.js + SQLite 的优势:
- 简单设置:SQLite 只是普通文件存储,无需运行复杂的数据库服务器。
- 客户端查询:可直接在 Next.js 应用中执行查询,无需额外后端。
- 离线持久化:用户短暂离线后数据依然存在。
- 可移植性:数据库文件可在不同环境与平台间轻松迁移。
- 可扩展性:SQLite 足够应付大多数简单查询需求,后期可平滑迁移到 Postgres、MySQL 等。
在快速原型阶段,SQLite 让你无需搭建独立数据库服务器即可验证核心功能。
2. 安装依赖
在 Next.js 中使用 SQLite,需安装以下依赖:
js
npm install sqlite3
npm install @mapbox/node-sqlite3
npm install sqlite
3. 创建数据库
在 pages/api
目录下新建 db.js
:
js
// pages/api/db.js
import sqlite3 from 'sqlite3'
import { open } from 'sqlite'
// 打开 SQLite 数据库连接
export async function openDb() {
return open({
filename: './mydb.db',
driver: sqlite3.Database
})
}
该文件会在项目根目录创建名为 mydb.db
的 SQLite 文件。若文件不存在,首次连接时会自动创建。
4. 定义表结构
在 pages/api
目录下创建 seed.js
,用于初始化数据库表:
js
// pages/api/seed.js
import { openDb } from './db'
async function setup() {
const db = await openDb()
// 创建 posts 表
await db.exec(`
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
content TEXT
);
`)
// 插入示例数据
await db.run(
'INSERT INTO posts (title, content) VALUES (?, ?)',
'Hello World',
'My first blog post!'
)
await db.close()
}
setup().catch(err => console.error(err.message))
运行脚本:
js
node pages/api/seed.js
至此,数据库与表已准备就绪。
5. 获取数据(服务端渲染)
在 pages/index.js
中利用 getServerSideProps
查询数据并传给组件:
js
// pages/index.js
import { openDb } from './api/db'
export async function getServerSideProps() {
const db = await openDb()
const posts = await db.all('SELECT * FROM posts')
await db.close()
return {
props: { posts }
}
}
function Home({ posts }) {
// 在 UI 中渲染 posts
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(p => (
<li key={p.id}>
<h2>{p.title}</h2>
<p>{p.content}</p>
</li>
))}
</ul>
</div>
)
}
export default Home
6. 实现 CRUD 操作
6.1 创建(Create)
pages/api/posts/create.js
:
js
import { openDb } from '../db'
export default async function handler(req, res) {
const { title, content } = req.body
const db = await openDb()
const result = await db.run(
'INSERT INTO posts (title, content) VALUES (?, ?)',
[title, content]
)
await db.close()
res.status(201).json(result)
}
6.2 读取(Read)
pages/api/posts/[id].js
:
js
import { openDb } from '../../db'
export default async function handler(req, res) {
const id = req.query.id
const db = await openDb()
const post = await db.get('SELECT * FROM posts WHERE id = ?', [id])
await db.close()
res.status(200).json(post)
}
6.3 更新(Update)
pages/api/posts/update.js
:
js
import { openDb } from '../db'
export default async function handler(req, res) {
const { id, title, content } = req.body
const db = await openDb()
await db.run(
`UPDATE posts SET title = ?, content = ? WHERE id = ?`,
[title, content, id]
)
await db.close()
res.status(200).json({ message: 'Post updated' })
}
6.4 删除(Delete)
pages/api/posts/delete.js
:
js
import { openDb } from '../db'
export default async function handler(req, res) {
const { id } = req.body
const db = await openDb()
await db.run('DELETE FROM posts WHERE id = ?', [id])
await db.close()
res.status(200).json({ message: 'Post deleted' })
}
7. 客户端数据获取(SWR/React Query)
暴露统一的 /api/posts
端点:
js
// pages/api/posts.js
import { openDb } from './db'
export default async function handler(req, res) {
const db = await openDb()
const posts = await db.all('SELECT * FROM posts')
await db.close()
res.status(200).json(posts)
}
在客户端组件中:
js
useEffect(() => {
async function fetchPosts() {
const res = await fetch('/api/posts')
const posts = await res.json()
// 更新组件状态
}
fetchPosts()
}, [])