Script Studio: Script Storage (SQL)
A script can keep its own data between executions, in tables it creates itself. This is useful for anything a script needs to remember that does not belong in Jira as a field or a comment — a counter, a cache, a record of what has already been processed.
Note: 📹 Video placeholder — creating a table, inserting a row on each run, and reading the totals back.
1. Creating a table
import { sql } from '@forge/sql';
await sql.executeDDL(`
CREATE TABLE IF NOT EXISTS note (
id INT NOT NULL AUTO_INCREMENT,
body VARCHAR(500) NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY (id)
)
`);
IF NOT EXISTS is worth using consistently: a script that creates its tables at the start of every run should not fail on the second run because the table is already there.
2. Reading and writing
import { sql } from '@forge/sql';
// A statement with parameters
await sql.prepare('INSERT INTO note (body, created_at) VALUES (?, ?)')
.bindParams('Escalated PROJ-142', new Date().toISOString())
.execute();
// A statement with none
const { rows } = await sql.executeRaw('SELECT id, body FROM note ORDER BY id DESC LIMIT 10');
for (const row of rows) {
console.log(row.id, row.body);
}
Note: Always pass values through
bindParams, using?placeholders in the statement. Never build a statement by joining text together with a value that came from an issue, an event or a caller — that is how a script becomes vulnerable to SQL injection.
3. Versioning a schema with the migration runner
For a table whose structure will change over time, migrationRunner applies a named set of statements once each, in order, and remembers which have already run — useful when several scripts, or several versions of one script, share the same tables.
import { migrationRunner } from '@forge/sql';
migrationRunner
.enqueue('001-create-note', `
CREATE TABLE IF NOT EXISTS note (
id INT NOT NULL AUTO_INCREMENT,
body VARCHAR(500) NOT NULL,
PRIMARY KEY (id)
)
`)
.enqueue('002-add-created-at', `
ALTER TABLE note ADD COLUMN created_at DATETIME NULL
`);
await migrationRunner.run();
A second run with the same names applies only the ones that have not already succeeded.
4. What is off-limits
Scripts share the same database as Script Studio itself, which stores workspaces, run history and version control there. To keep the two apart:
- Table names beginning
studio_, and a short list of specific legacy names, are reserved. A statement that names one of them is refused before it reaches the database. - The database's own system schemas (
information_schema,mysql,performance_schema,sys) are equally out of reach.
5. Limits
| Limit | Value |
|---|---|
| SQL statements per execution | 40 |
| Length of a single statement | 8,000 characters |
| Parameters per statement | 40 |
These sit inside the execution's own time limit; see Security and Governance for how long a script may run depending on how it was started.