Skip to content

API reference

Every tDB export, the oxmysql compatibility surface, the MySQL.lua library, console commands and events.

Every name below is reachable as exports.tDB:<name>, exports.oxmysql:<name>, exports['mysql-async']:<name> and exports.ghmattimysql:<name>.

Exports marked async return a promise: use the _async name, or Citizen.Await.

Readiness

ExportParametersReturnsAsync
isReadynonebooleanno
onReadycallbacknil. Fires immediately if already ready, else queuesno
isClientAuthenticatednoneboolean, live rather than the boot resultno
getUrlnonestring base URL, or nil before readyno
awaitConnectionnonetrueyes

A record or collection call made too early can still throw PocketBase client not ready yet. Wrap the first one:

lua
exports.tDB:onReady(function()
    local players = exports.tDB:getFullList('players')
end)

oxmysql exports

Each base name is registered three times: name, name_async and nameSync. All three behave identically.

Accepted call shapes: (sql), (sql, params), (sql, callback), (sql, params, callback). With a callback the export returns nothing and invokes it; without one it returns a promise.

ExportReturns
queryarray of rows
executearray of rows. Alias of query
fetcharray of rows. Alias of query
singlefirst row, or nil
scalarfirst column of the first row, or nil
insertthe new row's id
updateaffected row count
prepareone result per parameter set; a single set returns bare, and a callback unwraps a one-column row to the value
rawExecuteas prepare, without the unwrapping
transactiontrue, or false if it rolled back. Never throws
storethe query string unchanged. Synchronous, and works before readiness

mysql-async aliases

AliasBehaves asReturns
mysql_executeupdateaffected row count
mysql_insertinsertinsert id
mysql_fetch_allqueryrows
mysql_fetch_scalarscalarscalar
mysql_transactiontransactiontrue/false
mysql_storestorethe query

Query cache

Off by default. When on, row queries are cached and any write through tDB clears the cache.

ExportParametersReturns
enableCachettl = 60000, maxSize = 100true
disableCachenonetrue, and clears
invalidateCachepattern?: substring match on the key; omit to clear alltrue
setSlowQueryThresholdms: 0 disables the warning; default 150true

lib/MySQL.lua

Add server_script '@tDB/lib/MySQL.lua' to your fxmanifest.lua. Every entry has an .await form and a callable callback form, and waits for the database before dispatching, so a script querying during its own startup gets rows.

EntryReturns
MySQL.queryrows
MySQL.singlerow or nil
MySQL.scalarvalue
MySQL.insertinsert id
MySQL.updateaffected rows
MySQL.executerows (alias of query)
MySQL.fetchrows
MySQL.prepareper-set results
MySQL.rawExecuteraw result
MySQL.transactiontrue/false
lua
local rows = MySQL.query.await('SELECT * FROM players')
local id   = MySQL.insert.await('INSERT INTO logs (msg) VALUES (?)', { 'joined' })

MySQL.ready(function() print('database up') end)

Legacy namespaces: MySQL.Sync.execute (affected rows), MySQL.Sync.insert, MySQL.Sync.fetchAll, MySQL.Sync.fetchSingle, MySQL.Sync.fetchScalar, MySQL.Sync.transaction, MySQL.Sync.prepare, and the matching MySQL.Async.* callback forms. Also MySQL.ready, MySQL.isReady, MySQL.awaitConnection and MySQL.store.

Records

ExportParametersReturnsAsync
createcollection, data, options?created recordyes
updatecollection, id, data, options?updated recordyes
deletecollection, id?, options?trueyes
getOnecollection, id, options?record. Throws on 404yes
getListcollection, page = 1, perPage = 30, options?{ page, perPage, totalItems, totalPages, items }yes
getFullListcollection, options?array of recordsyes
getFirstListItemcollection, filter, options?record. Throws on 404yes
findRecordsByIdscollection, idsarray of recordsyes
countRecordscollection, filter?, params?numberyes
filterrawFilter, params?escaped filter stringno

Pass SQL as the first argument to update or delete and the call goes down the oxmysql path instead.

Build every filter with filter(), so values are bound rather than interpolated into the string:

lua
local f    = exports.tDB:filter('score > {:min} && banned = {:banned}', { min = 100, banned = false })
local top  = exports.tDB:getFullList('players', { filter = f })
local one  = exports.tDB:getFirstListItem('players', f)

Collections

ExportParametersReturnsAsync
getCollectionsoptions?arrayyes
getCollectionidOrName, options?collectionyes
createCollectiondata, options?collectionyes
updateCollectionidOrName, data, options?collectionyes
deleteCollectionidOrName, options?trueyes
truncateCollectionidOrName, options?trueyes
importCollectionscollections (table or JSON string), deleteMissing = false, options?trueyes
getCollectionScaffoldsoptions?scaffold mapyes
createMigrationHelpernonetable of createCollection, updateCollection, deleteCollection closuresno
lua
exports.tDB:createCollection({
    name = 'players',
    type = 'base',
    fields = {
        { name = 'username', type = 'text', required = true },
        { name = 'score',    type = 'number' },
        { name = 'banned',   type = 'bool' },
    },
})

Realtime

ExportParametersReturnsAsync
subscribecollection, topic, callback?trueyes
unsubscribecollection, topic?: omit to drop all for the collectiontrueyes
subscribeToTopictopic, callback?trueyes
unsubscribeFromTopictopictrueyes
unsubscribeByPrefixtopicPrefixtrueyes
isRealtimeConnectednonebooleanno
sendRealtimeMessagetopic, datanumber of clients sent toyes
getRealtimeClientsnonearray of { id, authenticated, authId }yes

Subscribe, then handle the event tDB raises. The name is pocketbase:<collection>:<topic> for a collection and pocketbase:topic:<topic> for a custom one:

lua
exports.tDB:subscribe('bans', '*')

AddEventHandler('pocketbase:bans:*', function(data)
    if data.action == 'create' then print(data.record.id) end
end)

Transactions and batches

ExportParametersReturnsAsync
runTransactionoperations: { type = "create"|"update"|"delete", collection, id?, data? }array: { id } per create/update, { deleted = true } per deleteyes
batchnonebatch handleno
batchCreatebatch, collection, data, options?the handle, chainableno
batchUpdatebatch, collection, id, data, options?the handleno
batchDeletebatch, collection, id, options?the handleno
batchUpsertbatch, collection, data, options?the handleno
batchSendbatchbatch result arrayyes

runTransaction runs inside a real database transaction and needs no setting. The batch* exports are PocketBase's batch API and must be enabled in Admin → Settings → Batch API first.

Raw SQL

The same database the oxmysql exports use, with {:name} placeholders and no MySQL translation — write SQLite here. All async.

ExportParametersReturns
sqlQuerysql, params?array of rows
sqlSinglesql, params?first row, or nil
sqlScalarsql, params?, columnName?the value, or nil
sqlExecutesql, params?insert id only
sqlTransactionqueries: { sql, params?, expect? }true; throws and rolls back the whole batch on failure

Integers arrive as numbers and NULL arrives as nil, not an empty string.

lua
local row = exports.tDB:sqlSingle('SELECT * FROM players WHERE id = {:id}', { id = 1 })

exports.tDB:sqlTransaction({
    { sql = 'UPDATE accounts SET balance = balance - {:n} WHERE id = {:a}', params = { n = 100, a = 'a' } },
    { sql = 'UPDATE accounts SET balance = balance + {:n} WHERE id = {:b}', params = { n = 100, b = 'b' } },
})

Auth

All async. Every one takes the auth collection name first.

ExportParametersReturns
listAuthMethodscollection, options?auth methods
authWithPasswordcollection, usernameOrEmail, password, options?{ token, record }
authCollectionWithPasswordsame{ token, record }
authWithOTPcollection, otpId, password, options?{ token, record }
authWithOAuth2Codecollection, provider, code, codeVerifier, redirectURL, createData?, options?{ token, record }
authRefresh / authRefreshCollectioncollection, options?{ token, record }
requestOTPcollection, email, options?{ otpId }
requestPasswordResetcollection, email, options?true
confirmPasswordResetcollection, token, password, passwordConfirm, options?true
requestVerificationcollection, email, options?true
confirmVerificationcollection, token, options?true
requestEmailChangecollection, newEmail, options?true
confirmEmailChangecollection, token, password, options?true
listExternalAuthscollection, recordId, options?array
unlinkExternalAuthcollection, recordId, provider, options?true

Files and mail

ExportParametersReturnsAsync
getFileUrlrecord, filename, options?URL stringno
getFileTokenoptions?token stringyes
sendEmailto (string or array), subject, html, text?trueyes

sendEmail needs mail configured in the admin UI — see Mail.

Commands

Server console only, all gated on the command.tdb ACE. See Importing .sql files for what each does in full.

CommandNotes
tdb sql statusEvery .sql file with its recorded state, plus ledger rows whose file is gone
tdb sql import <file>Applies one file now
tdb sql import <file> --force--force is recognised anywhere in the arguments
tdb sql tables [match]Every table with its row count, optionally filtered by substring
tdb sql query <sql>Runs a read query and prints the rows, 50 at a time
tdb sql planWhich tables would be handed to PocketBase, and how each id is handled
tdb sql adoptHands them over now, after taking a backup

Anything else prints usage: tdb sql status | import <file> [--force] | tables [match] | query <sql> | plan | adopt. Before the database is ready, every subcommand prints not ready yet - wait for the startup banner.

Events

All server-local. Use AddEventHandler.

EventPayload
pocketbase:<collection>:<topic>{ action = "create"|"update"|"delete", record }
pocketbase:topic:<topic>whatever was published to the topic
tdb:startupStatusa single string: the rendered startup box

Version facts

FactValue
Resource version1.0.0
PocketBase0.40.4, fetched and checksum-verified into .runtime/
tLib minimum1.0 (tlib_min_version)
Nodenode_version '22'
Providesoxmysql, mysql-async, ghmattimysql
Platformswindows_amd64, linux_amd64

Still stuck? Support can answer it directly.

Was this page helpful?