SQL
This content is for the 0.6.3 version. Switch to the latest version for up-to-date documentation.
SQL provides persistent database storage with SQLite-compatible syntax. You can execute queries, prepare statements, and manage data with familiar SQL commands.
The SQL component supports prepared statements for secure parameter binding. You get automatic query optimization and protection against SQL injection attacks.
Database operations return detailed metadata including execution time, rows affected, and database size changes.
Creating
Configure SQL databases in your raindrop.manifest file:
application "demo-sql-app" { sql_database "demosql" {}
service "demo-sql" { visibility = "public" }}
Accessing
Access SQL databases through environment bindings in your service code:
export default { async fetch(request: Request, env: Env) { // Access the configured SQL database const db = env.demosql;
// Execute a simple query const result = await db.exec("SELECT 1 as test"); return new Response(JSON.stringify(result)); }}
Core Concepts
Main interfaces for SQL operations:
SqlDatabase
- Primary database interface with prepare, batch, and exec methodsSqlPreparedStatement
- Prepared statement with bind, run, all, first, and raw methodsSqlResult
- Query results with data and metadataSqlResponse
- Base response with success status and operation metadataSqlExecResult
- Execution result for direct queriesSqlMeta
- Operation metadata with performance and change information
SqlResponse
Base response structure for successful SQL operations:
interface SqlResponse { // Indicates successful completion success: true; // Metadata about the operation meta: SqlMeta & Record<string, unknown>; // Error never present for successful operations error?: never;}
SqlResult
Query results containing data and metadata:
type SqlResult<T = unknown> = SqlResponse & { // Array of query results results: T[];};
SqlExecResult
Execution result for direct SQL statements:
interface SqlExecResult { // Number of statements executed count: number; // Duration of execution in milliseconds duration: number;}
SqlMeta
Operation metadata with performance information:
interface SqlMeta { // Duration of the operation in milliseconds duration: number; // Size of the database after the operation in bytes size_after: number; // Number of rows read during the operation rows_read: number; // Number of rows written during the operation rows_written: number; // ID of the last inserted row last_row_id: number; // Whether the database was modified changed_db: boolean; // Number of rows modified changes: number;}
System Limits
- Maximum database size: 10 GB
- Rate limit: 1000 queries per service
- SQL dialect: Subset of SQLite
prepare
// Prepare a SQL statement for executionprepare(query: string): SqlPreparedStatement
// Returns a prepared statement ready for executionSqlPreparedStatement
Creates a prepared statement from a SQL query string.
// Prepare an INSERT statement with parametersconst stmt = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
// Bind values and executeconst result = await stmt.bind("Alice", "alice@example.com").run();
batch
// Execute multiple prepared statements in a batchbatch<T = unknown>(statements: SqlPreparedStatement[]): Promise<SqlResult<T>[]>
// Returns array of results for each statementPromise<SqlResult<T>[]>
Executes multiple prepared statements in a single batch operation.
// Prepare multiple statementsconst insertUser = db.prepare("INSERT INTO users (name) VALUES (?)");const insertPost = db.prepare("INSERT INTO posts (title, user_id) VALUES (?, ?)");
// Execute as batchconst results = await db.batch([ insertUser.bind("Bob"), insertPost.bind("Hello World", 1)]);
exec
// Execute a SQL query directlyexec(query: string): Promise<SqlExecResult>
// Returns execution result with count and durationPromise<SqlExecResult>
Executes a SQL query directly without preparation.
// Create a table with direct executionconst result = await db.exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE )`);
console.log(`Executed ${result.count} statements in ${result.duration}ms`);
bind
// Bind values to the prepared statementbind(...values: unknown[]): SqlPreparedStatement
// Returns the prepared statement for chainingSqlPreparedStatement
Binds parameter values to a prepared statement.
// Bind parameters to a prepared statementconst stmt = db.prepare("SELECT * FROM users WHERE age > ? AND city = ?");const boundStmt = stmt.bind(25, "New York");
// Execute the bound statementconst results = await boundStmt.all();
first
// Get the first result column by namefirst<T = unknown>(colName: string): Promise<T | null>// Get the first result rowfirst<T = Record<string, unknown>>(): Promise<T | null>
// Returns the column value or row, or null if no resultsPromise<T | null>
Gets the first result column by name or the first result row.
// Get first column valueconst count = await db.prepare("SELECT COUNT(*) as total FROM users").first<number>("total");
// Get first rowconst user = await db.prepare("SELECT * FROM users WHERE id = ?").bind(1).first<User>();
run
// Execute the statement and return resultsrun<T = Record<string, unknown>>(): Promise<SqlResult<T>>
// Returns query results with metadataPromise<SqlResult<T>>
Executes the prepared statement and returns results with metadata.
// Execute an INSERT statementconst result = await db.prepare("INSERT INTO users (name) VALUES (?)") .bind("Charlie") .run();
// Check execution metadataconsole.log(`Inserted row ${result.meta.last_row_id}`);console.log(`Database changed: ${result.meta.changed_db}`);
all
// Execute the statement and return all resultsall<T = Record<string, unknown>>(): Promise<SqlResult<T>>
// Returns all query results with metadataPromise<SqlResult<T>>
Executes the prepared statement and returns all matching results.
// Get all users from the databaseconst result = await db.prepare("SELECT * FROM users WHERE active = ?") .bind(true) .all<User>();
// Access results arrayfor (const user of result.results) { console.log(`User: ${user.name}`);}
raw
// Get raw results with optional column namesraw<T = unknown[]>(options: { columnNames: true }): Promise<[string[], ...T[]]>raw<T = unknown[]>(options?: { columnNames?: false }): Promise<T[]>
// Returns raw results array with or without column namesPromise<[string[], ...T[]]> | Promise<T[]>
Gets raw query results as arrays with optional column names.
// Get raw results with column namesconst withColumns = await db.prepare("SELECT name, email FROM users") .raw({ columnNames: true });const [columns, ...rows] = withColumns;
// Get raw results without column namesconst rawRows = await db.prepare("SELECT name, email FROM users").raw();