Welcome to the world of using SQLite with Next.js! By the time you finish reading this guide, you’ll have a solid grasp of setting up and integrating an SQLite database into your Next.js applications.
Whether you’re building a simple side project, an MVP, or a full-blown web app, having the ability to store and retrieve data is crucial. SQLite is a highly portable, zero-configuration database that will allow you to get up and running quickly. The steps we cover make it a breeze to connect your Next.js frontend to a SQLite backend.
So without further ado, let’s dive in!
Working with SQLite across programming languages
- SQLite with NodeJS
- SQLite with Next.JS
- SQLite3 with Python
- SQLite with Python Flask
- SQLite with Python Django
- SQLite with Python AioSQLite
- SQLite with Python SQLAlchemy
- SQLite with Golang
- SQLite with Prisma
- SQLite with FastAPI
- SQLite with PHP
- SQLite for Expo Mobile Apps
- SQLite with React Native
- SQLite with PhoneGap
- OP-SQLite with React Native
- SQLite with C#
- SQLite with Javascript
- SQLite with R
- SQLite with Rust
Why Use SQLite in Next.js?
Next.js is a popular React framework for building server-rendered apps that scale. It handles routing, server-side rendering, code splitting automatically for you.
SQLite is a self-contained, file-based SQL database engine. It’s lightweight, fast, and requires zero configuration.
Benefits of using Next.js with SQLite:
- Simple setup – SQLite databases are just simple file stores. No need to run complex database servers.
- Client-side querying – Execute queries directly from your Next.js application without needing a backend server.
- Offline persistence – Data remains even when the user goes offline temporarily.
- Portability – The database file can be easily shared across environments and platforms.
- Scalability – SQLite handles most simple querying needs. And you can switch to heavier databases later.
For rapid prototyping, SQLite enables you to bypass setting up a separate database server just to test core functionality. As your web app evolves, you always have the flexibility to migrate to Postgres, MySQL or other DBs.
Now let’s jump into some code!
Installing Dependencies
To use SQLite in Next, we’ll need to install a few key dependencies:
npm install sqlite3
npm install @mapbox/node-sqlite3
npm install sqlite
This gives us everything we need on the server-side and client-side to work with an SQLite database.
Creating the Database
First, we need an actual database file. Inside pages/api
, create a db.js
file with the following:
// pages/api/db.js
import sqlite3 from 'sqlite3'
import { open } from 'sqlite'
// Open SQLite database connection
export async function openDb() {
return open({
filename: './mydb.db',
driver: sqlite3.Database
})
}
This creates a connection to a SQLite database named mydb.db
in the root folder of our project.
If the file does not exist, SQLite will automatically create it when opening a connection.
Note: This
openDb()
method provides access to the database connection from anywhere in our app.
Defining Tables
Before working with data, we need to define tables to store it.
Under pages/api
, create a seed.js
file responsible for setting up the database schema:
// pages/api/seed.js
import { openDb } from './db'
async function setup() {
// Open SQLite connection
const db = await openDb()
// Define table schema
await db.exec(`
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
content TEXT
);
`)
// Insert dummy data
await db.run(
'INSERT INTO posts (title, content) VALUES (?, ?)',
'Hello World',
'My first blog post!'
)
// Close connection
await db.close()
}
setup()
.catch(err => {
console.error(err.message)
})
This creates a posts
table with some dummy seed data.
The table has id
, title
, and content
fields. id
auto-increments to give each record a unique identifier.
Run this with:
node pages/api/seed.js
Our SQLite database and table are now ready!
Fetching Data
In Next.js, server-side data fetching occurs in the getServerSideProps
lifecycle method.
Under pages/index.js
, let’s query the posts from the database and pass them to our UI component:
// pages/index.js
import { openDb } from './api/db'
export async function getServerSideProps() {
// Open database
const db = await openDb()
// Get posts from database
const posts = await db.all('SELECT * FROM posts')
// Pass posts as prop to component
return {
props: {
posts
}
}
}
function Home({ posts }) {
// Render UI with posts
}
export default Home
That’s it! Our UI can now render the posts
fetched from SQLite.
Performing CRUD Operations
Let’s expose APIs for CRUD operations:
Create
To insert new records:
// pages/api/posts/create.js
import { openDb } from '../db'
async function handler(req, res) {
// Get post data from request body
const { title, content } = req.body
// Insert post into database
const db = await openDb()
const result = await db.run(
'INSERT INTO posts (title, content) VALUES (?, ?)',
[title, content]
)
await db.close()
// Return result to client
res.status(201).json(result)
}
export default handler
Read
To get a specific record by id:
// pages/api/posts/[id].js
import { openDb } from '../../db'
async function handler(req, res) {
// Get post ID from request URL
const id = req.query.id
// Get matching post from database
const db = await openDb()
const post = await db.get('SELECT * FROM posts WHERE id = ?', [id])
// Return result to client
res.status(200).json(post)
}
export default handler
Update
To modify existing records:
// pages/api/posts/update.js
import { openDb } from '../db'
async function handler(req, res) {
// Get ID and new title/content from body
const { id, title, content } = req.body
// Update matching record in database
const db = await openDb()
await db.run(
`UPDATE posts SET
title = ?,
content = ?
WHERE id = ?`,
[title, content, id]
)
await db.close()
// Return result to client
res.status(200).json({ message: 'Post updated' })
}
export default handler
Delete
To remove records:
// pages/api/posts/delete.js
import { openDb } from '../db'
async function handler(req, res) {
// Get ID from request body
const { id } = req.body
// Delete matching record from database
const db = await openDb()
await db.run('DELETE FROM posts WHERE id = ?', [id])
await db.close()
// Return result to client
res.status(200).json({ message: 'Post deleted' })
}
export default handler
And that’s the full CRUD workflow!
These API handlers allow Next.js to interface with the SQLite database for persistent storage.
Fetching Data on Client-Side
For client-side data fetching with SWR, React Query or RTK Query, expose a /api/posts
endpoint:
// pages/api/posts.js
import { openDb } from './db'
async function handler(req, res) {
const db = await openDb()
const posts = await db.all('SELECT * FROM posts')
res.status(200).json(posts)
}
export default handler
Now from any client component:
useEffect(() => {
async function fetchPosts() {
const res = await fetch('/api/posts')
const posts = await res.json()
// Update component state
}
fetchPosts()
}, [])
And that’s it! The SQLite data is now available for client-side consumption.
Take your Next.js skills to the next level
As you can see, combining Next.js and SQLite is incredibly powerful for building data-driven web applications.
The benefits don’t stop there. Mastering this stack also sets you up for success by teaching core concepts that translate to other frameworks.
Once you understand server-side rendering with Next.js, you can more easily pick up frameworks like Nuxt.js. And if you know how to leverage SQLite for simple schemas, transitioning to more complex databases like MongoDB becomes much easier.
So don’t just learn Next.js and SQLite in isolation – use them as springboards to accelerate your overall learning.
The web development landscape moves rapidly. But fundamentals like server-side rendering, database access patterns, REST API principles remain consistent.
If you take the time now to really nail down Next.js and SQLite, you’ll establish a foundation of core concepts for becoming a well-rounded, adaptable web developer no matter how the technology evolves.
So get out there and build something! With the power of Next.js and SQLite at your fingertips, you have everything you need to bring your ideas to life.
I can’t wait to see what you create next!