DEV Community

Cover image for Understanding SQLite PRAGMA (And How better-sqlite3 Makes It Nicer)
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Understanding SQLite PRAGMA (And How better-sqlite3 Makes It Nicer)

Hello, I'm Maneshwar. I'm working on FreeDevTools online currently building **one place for all dev tools, cheat codes, and TLDRs* — a free, open-source hub where developers can quickly find and use tools without any hassle of searching all over the internet.*

If you’ve ever worked with SQLite long enough, you’ve probably bumped into these odd-looking statements:

PRAGMA foreign_keys = ON;
PRAGMA journal_mode = WAL;
PRAGMA table_info(users);
Enter fullscreen mode Exit fullscreen mode

They look like SQL, but also… not exactly SQL.
They’re not part of your schema, not exactly configuration files, and not normal queries.

So what are PRAGMAs? Let’s unpack it in a clean, practical way.

What Exactly Is a PRAGMA in SQLite?

A PRAGMA is a special, SQLite-specific command used to:

  • configure how SQLite behaves
  • read internal metadata
  • perform maintenance tasks
  • change settings stored inside the database file
  • tweak performance characteristics

Think of PRAGMA as:
“internal switches and inspection tools built into SQLite.”

They’re not standard SQL, and they don’t apply to tables or columns.
You never “set a PRAGMA on a column.”
They only act on the SQLite engine or the database file.

Two Types of PRAGMAs (Important Distinction)

SQLite’s PRAGMAs fall into two buckets:

1. Config PRAGMAs (they change behavior)

Examples:

PRAGMA foreign_keys = ON;
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = 32000;
Enter fullscreen mode Exit fullscreen mode

These modify settings, often immediately, sometimes stored persistently.

2. Query/Info PRAGMAs (they return metadata)

Examples:

PRAGMA table_info('users');
PRAGMA index_list('orders');
PRAGMA page_size;
Enter fullscreen mode Exit fullscreen mode

These return rows — essentially virtual tables — but they don’t change anything.

Here’s a simple diagram of how SQLite handles PRAGMA internally:

How SQLite Processes a PRAGMA (Under the Hood)

When you run something like:

PRAGMA foreign_keys = ON;
Enter fullscreen mode Exit fullscreen mode

SQLite does this:

  1. Recognizes “PRAGMA” → not standard SQL
  2. Looks up the internal PRAGMA handler
  3. Sets an internal flag (db->flags.foreign_keys)
  4. Returns success

There is no schema change, no rewrite, no DDL.
It’s literally toggling switches inside SQLite’s internal C structs.

PRAGMAs That Persist vs. PRAGMAs That Don’t

Persist (stored in the .sqlite file)

(These are written to the database file header and will stay active the next time the DB is opened.)

  • journal_mode (when set to a persistent mode such as WAL or DELETE; modes like MEMORY or OFF do not persist)
  • auto_vacuum
  • page_size (but only before the first table is created)

Do NOT persist (per-connection)

  • foreign_keys
  • cache_size (unless using negative value for KiB mode)
  • temp_store

Very easy to get wrong.

Example:

PRAGMA foreign_keys = ON;
Enter fullscreen mode Exit fullscreen mode

→ works only for your connection, not globally.

The New Feature: PRAGMA as Table-Valued Functions

SQLite added something interesting:
Every query PRAGMA can also be accessed as a table-valued function.

Classic PRAGMA:

PRAGMA index_info('idx_users_email');
Enter fullscreen mode Exit fullscreen mode

Equivalent SELECT form:

SELECT * FROM pragma_index_info('idx_users_email');
Enter fullscreen mode Exit fullscreen mode

Why is this cool?
Because you can join, filter, order, limit, etc.

Example: list all indexed columns in the whole DB:

SELECT DISTINCT m.name || '.' || ii.name AS indexed_column
FROM sqlite_schema AS m
JOIN pragma_index_list(m.name) AS il
JOIN pragma_index_info(il.name) AS ii
WHERE m.type = 'table'
ORDER BY 1;
Enter fullscreen mode Exit fullscreen mode

You cannot do this kind of analysis with raw PRAGMA syntax.

Using PRAGMA in better-sqlite3

The PRAGMA functionality in SQLite works fine, but running it through SQL in Node can be messy.

Example:

db.prepare("PRAGMA cache_size").get(); 
Enter fullscreen mode Exit fullscreen mode

might return weird shapes like:

{ cache_size: 2000 }
Enter fullscreen mode Exit fullscreen mode

or sometimes:

{ 'cache_size(32000)': null }
Enter fullscreen mode Exit fullscreen mode

So better-sqlite3 fixes this with a dedicated method:

db.pragma("cache_size = 32000");
Enter fullscreen mode Exit fullscreen mode

And for reading:

db.pragma("cache_size", { simple: true });
Enter fullscreen mode Exit fullscreen mode

Output:

32000
Enter fullscreen mode Exit fullscreen mode

Much cleaner.

What better-sqlite3 .pragma() actually improves

1. Normalized return values

Consistent JS objects, unlike raw PRAGMA SQL.

2. Correct error handling

SQLite normally does not error on unknown PRAGMAs:

PRAGMA i_dont_exist;  -- silently ignored
Enter fullscreen mode Exit fullscreen mode

better-sqlite3:

db.pragma("i_dont_exist"); // throws Error
Enter fullscreen mode Exit fullscreen mode

Massive quality-of-life improvement.

3. Simple mode

Extracts first column of first row:

db.pragma("page_size", { simple: true }); // => 4096
Enter fullscreen mode Exit fullscreen mode

4. Safe for PRAGMAs with odd output formats

Some PRAGMAs return multiple columns, some return one, some return rows, some return nothing — this API smooths over that.

Useful PRAGMAs You’ll Actually Use

Enable foreign keys

PRAGMA foreign_keys = ON;
Enter fullscreen mode Exit fullscreen mode

Switch to WAL mode

PRAGMA journal_mode = WAL;
Enter fullscreen mode Exit fullscreen mode

Check table columns

PRAGMA table_info('users');
Enter fullscreen mode Exit fullscreen mode

Check indexes on a table

PRAGMA index_list('products');
Enter fullscreen mode Exit fullscreen mode

Get index columns

PRAGMA index_info('idx_products_name');
Enter fullscreen mode Exit fullscreen mode

Read DB page size

PRAGMA page_size;
Enter fullscreen mode Exit fullscreen mode

Maintenance / Schema Commands (Often used alongside PRAGMAs)

(Note: VACUUM is a DDL command, not a PRAGMA.)

VACUUM;
Enter fullscreen mode Exit fullscreen mode

Conclusion

PRAGMAs are one of the most powerful (and most misunderstood) parts of SQLite.
They’re not schema, not SQL extensions — they’re internal switches and metadata endpoints built inside SQLite itself.

And if you're using better-sqlite3, the .pragma() method gives you:

  • consistent output
  • better errors
  • simple mode
  • fewer head-scratching situations

Whether you’re tuning performance, introspecting schema, or enabling features like WAL mode, PRAGMAs are your main tool.

FreeDevTools

👉 Check out: FreeDevTools

Any feedback or contributors are welcome!

It’s online, open-source, and ready for anyone to use.

⭐ Star it on GitHub: freedevtools

Top comments (0)