Appwrite offers dedicated, native MySQL databases that you connect to directly over TLS. There is no Appwrite-specific driver or abstraction layer, so you can use Drizzle with the mysql2 driver through the drizzle-orm/mysql2 package, the same way you would with any MySQL server.
To get your connection string, open your database in the Appwrite Console and click Credentials, or fetch it from the Appwrite API with mysql.get(). The username is admin.<hash>, where the hash matches the one in the hostname, and the database name is always default. Appwrite’s proxy routes your connection by this username, so always use the full value from the connection string.
bun add drizzle-orm@rc mysql2bun add -D drizzle-kit@rc
Step 2 - Initialize the driver and make a query
mysql2
mysql2 with config
import { drizzle } from "drizzle-orm/mysql2";const db = drizzle(process.env.DATABASE_URL);const response = await db.select().from(...)
import { drizzle } from "drizzle-orm/mysql2";// You can specify any property from the mysql2 connection optionsconst db = drizzle({ connection: { uri: process.env.DATABASE_URL, ssl: { rejectUnauthorized: true } } });const response = await db.select().from(...)
If you need to provide your existing driver:
Client connection
Pool connection
import { drizzle } from "drizzle-orm/mysql2";import mysql from "mysql2/promise";const connection = await mysql.createConnection(process.env.DATABASE_URL);const db = drizzle({ client: connection });
import { drizzle } from "drizzle-orm/mysql2";import mysql from "mysql2/promise";const poolConnection = mysql.createPool({ uri: process.env.DATABASE_URL,});const db = drizzle({ client: poolConnection });
IMPORTANT
Connections on Appwrite Cloud are encrypted with TLS. If your runtime or mysql2 configuration does not infer TLS from the connection string, pass ssl: { rejectUnauthorized: true } in the connection options.
For the built in migrate function with DDL migrations we and drivers strongly encourage you to use a single client connection.
Connection pooling for serverless
Appwrite runs a connection pooler on port 6033 of the same hostname. In serverless and edge environments, route your application traffic through the pooler so short-lived instances don’t exhaust the engine’s connection limit, and keep Drizzle Kit pointed at the direct engine port 3306, because migrations issue DDL that needs a session-level connection.
# Runtime: pooled, transaction modeDATABASE_URL="mysql://admin.<hash>:<password>@db-<hash>.<region>.appwrite.center:6033/default?ssl=true"# Migrations & introspection: direct connection to the engineDIRECT_URL="mysql://admin.<hash>:<password>@db-<hash>.<region>.appwrite.center:3306/default?ssl=true"
The pooler defaults to transaction mode, which does not keep a backend connection across statements. Drizzle’s regular query builder sends text queries and works with transaction pooling, but session-level features such as explicit prepared queries, user variables and temporary tables need the direct port or a session-mode pooler.