如何在SvelteKit中集成sqlite3?
SvelteKit 集成 SQLite3 落地实现方案
核心前提:SQLite 是嵌入式服务端数据库,所有数据库操作仅能在 SvelteKit 服务端代码(+page.server.js/ts、+server.js/ts、hooks、src/lib/server/ 目录)运行,绝对禁止在浏览器前端代码中导入相关模块,否则会触发原生模块加载报错。
依赖选型
- 稳定生产场景优先选
better-sqlite3:同步API风格、性能优异、不需要写冗余的异步回调,和轻量全栈项目的匹配度最高 - Node 22及以上版本可直接使用内置的
node:sqlite模块,无需安装第三方依赖,仅适配新Node版本的运行环境 - 不推荐传统的
sqlite3npm包:异步回调风格写起来繁琐,性能和同步API版本差距明显
初始化全局数据库连接
SvelteKit开发模式下会频繁热更新模块,如果每次请求都新建数据库连接,很快会触发系统文件句柄耗尽报错,因此需要把连接实例挂到全局对象上做缓存。
- 新建
src/lib/server/db/index.js文件(SvelteKit会自动给server目录下的代码加服务端边界,不会打包到前端产物) - 写入连接初始化逻辑:
// src/lib/server/db/index.js import Database from 'better-sqlite3'; import { dev } from '$app/environments'; // 全局缓存实例,避免热更新重复建连 const globalForDb = globalThis; // 数据库文件存储路径 const dbPath = dev ? './data/dev.db' : './data/prod.db'; // 初始化连接 const db = globalForDb.db ?? new Database(dbPath); // 开启WAL模式提升读写并发性能,打开外键约束 db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = ON'); // 开发环境下把实例挂到全局 if (dev) globalForDb.db = db; // 项目表结构初始化逻辑直接在这里执行,示例: db.exec(` CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // 封装常用的操作方法,减少重复写prepare的代码 export const get = (sql, params = []) => db.prepare(sql).get(...params); export const query = (sql, params = []) => db.prepare(sql).all(...params); export const run = (sql, params = []) => db.prepare(sql).run(...params);
- 配套配置:
- 项目根目录新建
data文件夹,用于存放数据库文件 - 在
.gitignore中加入*.db、*.db-wal、*.db-shm,避免把数据库文件提交到代码仓库 - 确保运行SvelteKit的系统账号对
data目录有读写权限
构建配置调整
因为 better-sqlite3 是原生Node模块,不能被Vite打包到bundle里,需要在 svelte.config.js 中标记为外置依赖:
// svelte.config.js const config = { kit: { // 适配你选的部署适配器 adapter: YourAdapter, // 原生模块不参与打包 external: ['better-sqlite3'] } }; export default config;
路由层调用示例
页面服务端加载(+page.server.js)
// src/routes/posts/+page.server.js import { query, run } from '$lib/server/db'; import { error } from '@sveltejs/kit'; // 页面加载时直接查数据库拿数据 export async function load() { const postList = query('SELECT id, title, created_at FROM posts ORDER BY created_at DESC'); return { postList }; } // 处理表单提交动作 export const actions = { createPost: async ({ request }) => { const formData = await request.formData(); const title = formData.get('title'); const content = formData.get('content'); if (!title?.trim() || !content?.trim()) throw error(400, '标题和内容不能为空'); run( 'INSERT INTO posts (title, content) VALUES (?, ?)', [title, content] ); } }
独立API路由(+server.js)
// src/routes/api/posts/+server.js import { json } from '@sveltejs/kit'; import { query } from '$lib/server/db'; export function GET() { const postList = query('SELECT id, title, created_at FROM posts'); return json(postList); }
常见踩坑说明
- 开发模式频繁热更新导致的文件句柄泄露:只要按全局实例缓存的方案配置,就不会出现重复建连的问题
- 生产构建报错“找不到better-sqlite3模块”:90%的原因是把数据库相关代码导入到了非服务端上下文(比如放了
src/lib/下而不是src/lib/server/),检查所有导入db的文件位置,确保所有调用都在服务端边界内 - 部署到只读文件系统的云平台(比如部分Serverless平台)时,本地磁盘目录没有写权限,需要把数据库文件路径临时改到系统指定的临时可写目录(比如Linux下的
/tmp),这种场景其实更推荐用专门的云数据库,SQLite的体验会打折扣
内容的提问来源于stack exchange,提问作者Anil Bhattarai
相关产品推荐
相关产品推荐

