The Preview version of Forge SQL currently does not support data residency. By using Forge SQL, you acknowledge that Section 6 (Data Residency) of the Forge Terms does not apply to data stored using Forge SQL, and you must not use Forge SQL for any applications that require data residency. For more details, please see the Data residency limitations section.
Use Data Definition Language (DDL) to create and update database objects (such as tables and indexes) in your provisioned SQL database. Forge SQL supports MySQL-compatible DDL operations like CREATE
, ALTER
, and DROP
(for more details, see
SQL Statement Overview in the TiDB documentation).
You can create as many DDL operations as needed. Forge SQL can use scheduledTrigger
to execute each operation on each provisioned SQL database in the sequence you specify.
You can also update your app’s database schema by adding new DDL operations over time. Forge SQL can:
The sql
package provides the necessary methods for interacting with Forge SQL. To start using Forge SQL’s capabilities, you’ll need to install it in your project:
1 2npm install @forge/sql
You'll need the migrationRunner
SDK to execute DDL operations. To import it:
1 2import migrationRunner from `@forge/sql` ;
Use DDL operations to create and update each database object in your schema. Use the migrationRunner.enqueue
method to queue these operations in the order they should be executed. This method accepts a list of ordered DDL operations, with each one defined as a key/value pair consisting of:
operationName
The following snippet defines two DDL operations, CREATE_USERS_TABLE
and CREATE_BOOKS_TABLE
, both of which create tables for our provisioned database.
1 2import { migrationRunner } from '@forge/sql'; export const CREATE_USERS_TABLE = `CREATE TABLE IF NOT EXISTS Users ( user_id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, )`; export const CREATE_BOOKS_TABLE = `CREATE TABLE IF NOT EXISTS Books ( book_id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, author VARCHAR(100) NOT NULL, published_date DATE )`;
To invoke both DDL operations through migrationRunner.enqueue
, assign an operationName
to each one (v001_create_users_table
and v002_create_books_table
):
1 2const createDBobjects = migrationRunner .enqueue('v001_create_users_table', CREATE_USERS_TABLE) .enqueue('v002_create_books_table', CREATE_BOOKS_TABLE)
Next, wrap createDBobjects
in a single database object creation function (runMigration
). This will let you map its key to a scheduled trigger, which Forge will use to execute it (this is covered in the next section):
1 2export const runMigration = async () => { try { await applyMigrations(); }; export const applyMigrations = async () => { const migrationsRun = await createDBobjects.run(); };
Creating database objects
Our example app uses DDL operations to define each database object, then orders them in the sequence they should be executed. This sequence is then wrapped in a single database object creation function.
Database object creation (that is, your DDL operations) needs to be executed as part of the app installation process. One way to orchestrate this is through scheduled triggers. Doing so assigns the entire lifecycle of creating your database objects to Forge SQL.
Map your database object creation function to a
scheduled trigger module in your manifest. Forge will use the trigger to execute your migrationRunner
invocation according to your defined interval
(we recommend hourly
or daily
).
The following declaration triggers the runMigration
function from the
previous example:
1 2modules: scheduledTrigger: - key: my-db-schema function: runMigration interval: hour function: - key: runMigration handler: index.trigger
Here, Forge will execute runMigration
within the hour after app installation. As such, it is possible for customers to have your app already installed, without the database schema applied yet.
Forge SQL will check each app installation hourly if there are any failed or pending DDL functions (tracking them based on their operationName
). Forge SQL will run pending DDL functions and re-run failed ones.
Orchestrate DDL operations
In our sample app, the database object creation function is mapped to a scheduled trigger. This lets Forge manage the lifecycle of your database object creation (and database schema migration later on, if needed).
The migrationRunner.list
method lists all the DDL operations you queued (through migrationRunner.enqueue
), along with the status of each one. Use it to generate logs for your database object creation function; this will allow you to track its progress for each app installation.
Use your app logs in the Developer Console to view these generated logs. From there, you can filter for errors against your database object creation function.
The following snippet expands on our earlier example by adding a migrationRunner.list
invocation to create logs for the runMigration
function:
1 2export const applyMigrations = async () => { const migrationsRun = await createDBobjects.run(); console.log('Migrations checkpoint [after running migrations]:'); await migrationRunner .list() .then((migration) => migration.map((y) => console.log(`${y.name} migrated at ${y.migratedAt.toUTCString()}`))); };
Generate logs
Our sample app’s database object creation function generates logs for each operation. These logs let you track the progress of each schema application (and update) for every app installation.
You can inspect each installation's SQL database schema through the developer console. The developer console can display:
See Monitoring SQL for more information.
Your app should only have one migrationRunner
invocation.
Ensure that each change to your SQL database is backwards compatible to all schema versions that are currently in use. Every DDL operation you define should introduce schema changes that won’t block data migrations from previous versions.
Likewise, each SQL database change should be compatible to all versions of your app currently installed on a customer site. This means, for example, that every SQL query used by previous versions of your app should also work in the latest version of your SQL database.
Avoid destructive changes to your SQL database, as these risk breaking compatibility between database schema versions.
Rate this page: