Clarify afterCreate Usage for SQLite Dialects
- Dominant language
- Vue
- Stars
- 399
- Forks
- 232
- PR merge metrics
- No merged PRs in 30d
Description
Users are confused about the differences in `afterCreate` pool callback implementation between `sqlite3` and `better-sqlite3` drivers. Issue [4971](https://github.com/knex/knex/issues/4971) shows users copying sqlite3 examples that crash with better-sqlite3 due to different APIs.
## Current Documentation Gaps
### 1. No Clear Differentiation
The documentation shows a generic PostgreSQL example but doesn't clarify the differences between SQLite drivers:
- `sqlite3` uses callback-style APIs (`.run()`, `.exec()` with callbacks)
- `better-sqlite3` uses synchronous APIs (`.pragma()`, `.prepare()`, no callbacks)
### 2. Missing Common Use Cases
No examples for the most common SQLite configurations:
- Enabling foreign keys
- Setting journal mode
- Configuring busy timeout
- Setting cache size
### 3. Incorrect Test Examples
Even the test files show inconsistent patterns:
- `/test/knexfile.js:31` uses `connection.run('PRAGMA foreign_keys = ON', callback)` for sqlite3
- `/test/knexfile.js:41` uses `connection.prepare('PRAGMA foreign_keys = ON').run()` for better-sqlite3
## Documentation Changes Required
### 1. Update Main Pool Documentation (`docs/src/guide/index.md`)
Add a new section after the current `afterCreate` example (line 387):
```markdown
#### Driver-Specific afterCreate Examples
##### PostgreSQL / MySQL
```js
const knex = require('knex')({
client: 'pg', // or 'mysql', 'mysql2'
connection: {...},
pool: {
afterCreate: function (conn, done) {
// Callback-based API
conn.query('SET timezone="UTC";', function (err) {
if (err) {
// Connection will be discarded
done(err, conn);
} else {
// Connection is ready
done(null, conn);
}
});
}
}
});
```
##### SQLite3 (callback-based)
```js
const knex = require('knex')({
client: 'sqlite3',
connection: {
filename: './mydb.sqlite'
},
pool: {
afterCreate: function (conn, done) {
// sqlite3 uses callback-style methods
conn.run('PRAGMA foreign_keys = ON', done);
}
}
});
```
##### Better-SQLite3 (synchronous)
```js
const knex = require('knex')({
client: 'better-sqlite3',
connection: {
filename: './mydb.sqlite'
},
pool: {
afterCreate: function (conn, done) {
// better-sqlite3 uses synchronous methods
try {
conn.pragma('foreign_keys = ON');
// For other PRAGMA statements:
// conn.pragma('journal_mode = WAL');
// conn.pragma('busy_timeout = 5000');
done(null, conn);
} catch (err) {
done(err, conn);
}
}
}
});
```
##### Common SQLite Configurations
```js
// For sqlite3
pool: {
afterCreate: function (conn, done) {
conn.serialize(function() {
conn.run('PRAGMA foreign_keys = ON');
conn.run('PRAGMA journal_mode = WAL');
conn.run('PRAGMA synchronous = NORMAL');
conn.run('PRAGMA cache_size = -64000');
conn.run('PRAGMA busy_timeout = 5000', done);
});
}
}
// For better-sqlite3
pool: {
afterCreate: function (conn, done) {
try {
conn.pragma('foreign_keys = ON');
conn.pragma('journal_mode = WAL');
conn.pragma('synchronous = NORMAL');
conn.pragma('cache_size = -64000');
conn.pragma('busy_timeout = 5000');
done(null, conn);
} catch (err) {
done(err, conn);
}
}
}
```
### 2. Add Warning Box
Add after the driver examples:
```markdown
:::warning SQLite Driver Differences
**Important:** `sqlite3` and `better-sqlite3` have different APIs:
- **sqlite3**: Uses Node.js callback-style async methods (`.run()`, `.exec()`, `.all()`)
- **better-sqlite3**: Uses synchronous methods (`.pragma()`, `.prepare()`, `.exec()`)
Copying `afterCreate` code between these drivers will cause errors. Always use the appropriate API for your chosen driver.
:::
```
### 3. Update SQLite Section
In the SQLite configuration section (around line 90-160), add:
```markdown
#### Choosing Between sqlite3 and better-sqlite3
| Feature | sqlite3 | better-sqlite3 |
|---------|---------|----------------|
| API Style | Asynchronous (callbacks/promises) | Synchronous |
| Performance | Good for concurrent operations | Faster for sequential operations |
| afterCreate | Uses callbacks | Synchronous, but still needs done() callback |
| Common Methods | `.run()`, `.get()`, `.all()` | `.prepare()`, `.pragma()` |
| Foreign Keys | `conn.run('PRAGMA foreign_keys = ON', cb)` | `conn.pragma('foreign_keys = ON')` |
```
### 4. Update FAQ/Recipes (`docs/src/faq/recipes.md`)
Update the SQLCipher example (line 76) to show both variants:
```markdown
#### SQLCipher with sqlite3
```js
const knex = require('knex')({
client: 'sqlite3',
connection: {
filename: 'file:foobar.db?cipher=sqlcipher'
},
pool: {
afterCreate: function (conn, done) {
conn.run("PRAGMA KEY = 'secret'", done);
}
}
});
```
#### SQLCipher with better-sqlite3
```js
const knex = require('knex')({
client: 'better-sqlite3',
connection: {
filename: 'file:foobar.db?cipher=sqlcipher'
},
pool: {
afterCreate: function (conn, done) {
try {
conn.pragma("KEY = 'secret'");
done(null, conn);
} catch (err) {
done(err, conn);
}
}
}
});
```
### 5. Add Migration Guide
Create a new section in the documentation:
```markdown
## Migrating from sqlite3 to better-sqlite3
If you're switching from `sqlite3` to `better-sqlite3`, update your `afterCreate`:
**Before (sqlite3):**
```js
afterCreate: function (conn, done) {
conn.run('PRAGMA foreign_keys = ON', done);
}
```
**After (better-sqlite3):**
```js
afterCreate: function (conn, done) {
try {
conn.pragma('foreign_keys = ON');
done(null, conn);
} catch (err) {
done(err, conn);
}
}
```
## Additional Recommendations
1. **Add TypeScript definitions** that show the different connection types for each driver
2. **Create a troubleshooting section** for common errors like "conn.run is not a function"
3. **Add links to driver documentation**:
- [sqlite3 API](https://github.com/TryGhost/node-sqlite3/wiki/API)
- [better-sqlite3 API](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md)
## Success Criteria
After implementing these changes:
1. Users should immediately understand which code to use for their chosen SQLite driver
2. Copy-paste errors between drivers should be eliminated
3. Common PRAGMA configurations should be easily discoverable
4. The error "conn.run is not a function" should lead users directly to the solution
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the afterCreate example in docs/src/guide/index.md and the SQLCipher recipe in docs/src/faq/recipes.md. Compare the sqlite3 and better-sqlite3 patterns shown in test/knexfile.js, then update the requested examples, warning, configuration guidance, and migration section so each driver’s API and common PRAGMA setup are clear.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, sqlite
- Domain
- database, documentation
- Issue type
- Documentation
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100