Prisma_
Use Prisma ORM with an Appwrite native MySQL database. Configure the Prisma 7 datasource, apply schema changes through the direct connection, and instantiate Prisma Client with the MySQL driver adapter.
4 min read
Prisma ORM works with Appwrite's native MySQL database as a standard MySQL target. Configure Prisma with the connection string from Appwrite, apply schema changes through the direct database connection, and use Prisma Client from your application code.
You'll need a native MySQL database in a ready state and its credentials. The database object returned by the Appwrite API includes hostname, connectionUser, connectionPassword, and connectionString; you can read it with mysql.get() or from the response when you create the database.
Initialize Prisma
Install Prisma, Prisma Client, the MySQL driver adapter, the MariaDB driver used by the adapter, and the TypeScript tools used by the examples:
npm install -D prisma typescript tsx @types/nodenpm install @prisma/client @prisma/adapter-mariadb dotenv mariadbInitialize Prisma for MySQL:
npx prisma init --datasource-provider mysql --output ../generated/prismaPrisma 7 generates a prisma.config.ts file and a prisma/schema.prisma file. Set "type": "module" in package.json if your project does not already use ECMAScript modules.
Configure connection strings
Store the Appwrite connection string in environment variables and do not commit it:
DATABASE_URL="mysql://admin:<password>@db-<hash>.<region>.appwrite.center:3306/<database>?sslaccept=strict"DIRECT_URL="mysql://admin:<password>@db-<hash>.<region>.appwrite.center:3306/<database>?sslaccept=strict"DATABASE_URL is the runtime connection used by Prisma Client. DIRECT_URL is the direct database connection used by Prisma CLI commands.
Appwrite Cloud uses TLS for MySQL connections. Prisma's MySQL connector uses sslaccept=strict for TLS with certificate verification. For mTLS and network controls, see Network security.
Prisma's migrate dev command uses a shadow database to detect drift. Appwrite's MySQL admin user is scoped to its database and cannot create another MySQL database for that workflow. Generate development migrations against a local MySQL database, a separate Appwrite database, or an Appwrite branch, then apply committed migrations to this database with migrate deploy.
Configure Prisma
In prisma.config.ts, read the CLI connection string from the environment:
import "dotenv/config";import { defineConfig } from "prisma/config";
export default defineConfig({ schema: "prisma/schema.prisma", migrations: { path: "prisma/migrations", seed: "tsx prisma/seed.ts", }, datasource: { url: process.env["DIRECT_URL"] ?? process.env["DATABASE_URL"], },});In prisma/schema.prisma, keep the datasource provider in the schema file and generate Prisma Client into the output directory created by prisma init:
generator client { provider = "prisma-client" output = "../generated/prisma"}
datasource db { provider = "mysql"}
model User { id String @id @default(uuid()) email String @unique createdAt DateTime @default(now())
@@map("prisma_User")}Rename the mapped table for your application. If you are connecting to an existing database, use Prisma introspection after you configure the environment variables so your schema matches the current tables.
Pool connections from serverless
Prisma Client opens database connections from each running application instance. On serverless platforms, many cold starts can quickly multiply the number of backend MySQL connections. On specifications that include the connection pooler, route runtime traffic through the pooler port, 6033, for DATABASE_URL while keeping DIRECT_URL on the direct MySQL port, 3306, for migrations and introspection:
# Runtime: pooled connection through the Appwrite connection poolerDATABASE_URL="mysql://admin:<password>@db-<hash>.<region>.appwrite.center:6033/<database>?sslaccept=strict"
# Prisma CLI: direct MySQL connection for migrations and introspectionDIRECT_URL="mysql://admin:<password>@db-<hash>.<region>.appwrite.center:3306/<database>?sslaccept=strict"The pooler defaults to transaction mode, which gives the highest connection multiplexing. If your application depends on session-level features such as user variables, temporary tables, or session-scoped prepared statements, use session mode or keep that workload on the direct MySQL port. See Connection pooling for pool mode trade-offs.
Apply schema changes
For an initial migration generated from the Prisma schema file, create a migration directory and write the SQL diff:
mkdir -p prisma/migrations/20260708160000_initnpx prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script --output prisma/migrations/20260708160000_init/migration.sqlIf this database already contains tables and Prisma has not created its migration history, baseline it once before applying migrations:
mkdir -p prisma/migrations/00000000000000_baselinetouch prisma/migrations/00000000000000_baseline/migration.sqlnpx prisma migrate resolve --applied 00000000000000_baselineApply committed migrations through the direct MySQL connection:
npx prisma migrate deployGenerate Prisma Client after you install dependencies or change prisma/schema.prisma:
npx prisma generateThe primary admin user owns its assigned database and can run schema changes inside that database. Keep migrations and introspection on the direct MySQL port because they need session-level behavior.
Seed data
With the seed command configured in prisma.config.ts, add a seed script:
import "dotenv/config";import { PrismaMariaDb } from "@prisma/adapter-mariadb";import { PrismaClient } from "../generated/prisma/client";
const adapter = new PrismaMariaDb(process.env.DATABASE_URL!);const prisma = new PrismaClient({ adapter });
await prisma.user.upsert({ where: { email: "ada@example.com" }, update: {}, create: { email: "ada@example.com" },});
await prisma.$disconnect();Run the seed command:
npx prisma db seedQuery with Prisma Client
Instantiate Prisma Client with the MySQL driver adapter:
import "dotenv/config";import { PrismaMariaDb } from "@prisma/adapter-mariadb";import { PrismaClient } from "./generated/prisma/client";
const adapter = new PrismaMariaDb(process.env.DATABASE_URL!);const prisma = new PrismaClient({ adapter });
const user = await prisma.user.create({ data: { email: "grace@example.com" },});
const recent = await prisma.user.findMany({ orderBy: { createdAt: "desc" }, take: 10,});
console.log({ user, recent });
await prisma.$disconnect();On long-running servers, instantiate PrismaClient once and reuse it. On serverless platforms, keep a single client per module scope so warm invocations reuse it, and rely on the pooler to absorb cold-start connection churn when your database specification includes it.
Use a branch for previews and CI
Branches are instant, isolated copies of a database with their own hostname and connection string. They are useful for running migrations against throwaway data in a pull-request preview or integration-test job:
- Create a branch from the API and read its
connectionString. - Export it as
DIRECT_URL, and use the pooled variant asDATABASE_URLif the branch has the pooler enabled. - Run
npx prisma migrate deployand your test suite against the branch. - Delete the branch when the job finishes.
Because a branch starts from a storage snapshot, the schema and data match the source database at branch time, so migrations run against realistic data without touching production.
Related
Connections
Retrieve credentials, rotate the primary password, and connect with MySQL tools.
Connection pooling
Pool modes, ports, and read/write splitting for serverless workloads.
Branches
Ephemeral database copies for preview environments and CI.
Network security
TLS modes, certificate verification, mTLS, and IP allowlists.
Was this page helpful?
Share what worked or what we should fix. Once approved, our agents automatically apply suggested updates to the docs.