Skip to content

Multi-Database Support

Multi-Database Support allows you to use any database with Flowfull through Kysely ORM. Switch databases without changing your code.

Supported Databases

Flowfull supports multiple database types:

DatabaseTypeUse Case
PostgreSQLTraditionalProduction, complex queries
MySQLTraditionalProduction, compatibility
LibSQL/TursoServerlessEdge deployments, low latency
NeonServerlessServerless Postgres
PlanetScaleServerlessServerless MySQL

Configuration

PostgreSQL

env
DATABASE_TYPE=postgresql
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

MySQL

env
DATABASE_TYPE=mysql
DATABASE_URL=mysql://user:password@localhost:3306/mydb

LibSQL/Turso

env
DATABASE_TYPE=libsql
DATABASE_URL=libsql://your-database.turso.io
DATABASE_AUTH_TOKEN=your-auth-token

Neon (Serverless Postgres)

env
DATABASE_TYPE=postgresql
DATABASE_URL=postgresql://user:password@ep-xxx.neon.tech/mydb?sslmode=require

PlanetScale (Serverless MySQL)

env
DATABASE_TYPE=mysql
DATABASE_URL=mysql://user:password@aws.connect.psdb.cloud/mydb?ssl={"rejectUnauthorized":true}

Usage

The same code works with all databases:

typescript
import { db } from './lib/database';

// Query works with any database
const users = await db
  .selectFrom('users')
  .where('email', '=', 'user@example.com')
  .selectAll()
  .execute();

// Insert works with any database
await db
  .insertInto('users')
  .values({
    id: nanoid(),
    email: 'user@example.com',
    name: 'John Doe'
  })
  .execute();

// Update works with any database
await db
  .updateTable('users')
  .set({ name: 'Jane Doe' })
  .where('id', '=', userId)
  .execute();

// Delete works with any database
await db
  .deleteFrom('users')
  .where('id', '=', userId)
  .execute();

Kysely ORM

Flowfull uses Kysely for database abstraction:

Benefits

  • ✅ Type-safe queries
  • ✅ Works with multiple databases
  • ✅ No code generation
  • ✅ Excellent TypeScript support
  • ✅ Lightweight and fast

Example

typescript
// Type-safe query
const user = await db
  .selectFrom('users')
  .where('id', '=', userId)
  .select(['id', 'email', 'name'])
  .executeTakeFirst();

// TypeScript knows the shape of `user`
console.log(user.email);  // ✅ Type-safe
console.log(user.invalid); // ❌ TypeScript error

Migrations

Migrations work the same across all databases:

typescript
// migrations/001_create_users.ts
import { Kysely } from 'kysely';

export async function up(db: Kysely<any>): Promise<void> {
  await db.schema
    .createTable('users')
    .addColumn('id', 'varchar(255)', (col) => col.primaryKey())
    .addColumn('email', 'varchar(255)', (col) => col.notNull().unique())
    .addColumn('name', 'varchar(255)', (col) => col.notNull())
    .addColumn('created_at', 'timestamp', (col) => col.notNull())
    .execute();
}

export async function down(db: Kysely<any>): Promise<void> {
  await db.schema.dropTable('users').execute();
}

Run migrations:

bash
bun run migrate

Switching Databases

To switch databases, just change the environment variables:

env
# From PostgreSQL
DATABASE_TYPE=postgresql
DATABASE_URL=postgresql://localhost:5432/mydb

# To MySQL
DATABASE_TYPE=mysql
DATABASE_URL=mysql://localhost:3306/mydb

# To LibSQL/Turso
DATABASE_TYPE=libsql
DATABASE_URL=libsql://your-database.turso.io
DATABASE_AUTH_TOKEN=your-token

No code changes required! 🎉

Best Practices

✅ Do

  • Use PostgreSQL for complex queries
  • Use LibSQL/Turso for edge deployments
  • Use Neon/PlanetScale for serverless
  • Write database-agnostic queries
  • Test with multiple databases

❌ Don't

  • Use database-specific SQL
  • Hardcode database type
  • Skip migrations
  • Forget to backup data
  • Mix database types in production

Database-Specific Features

PostgreSQL

typescript
// JSON queries
const users = await db
  .selectFrom('users')
  .where('metadata->email_verified', '=', 'true')
  .selectAll()
  .execute();

// Full-text search
const results = await db
  .selectFrom('posts')
  .where(sql`to_tsvector(title) @@ to_tsquery(${query})`)
  .selectAll()
  .execute();

MySQL

typescript
// JSON queries
const users = await db
  .selectFrom('users')
  .where(sql`JSON_EXTRACT(metadata, '$.email_verified')`, '=', 'true')
  .selectAll()
  .execute();

LibSQL/Turso

typescript
// Edge-optimized queries
const users = await db
  .selectFrom('users')
  .where('region', '=', 'us-east')
  .selectAll()
  .execute();

Connection Pooling

PostgreSQL/MySQL

typescript
import { Pool } from 'pg';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,  // Max connections
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

LibSQL/Turso

typescript
import { createClient } from '@libsql/client';

const client = createClient({
  url: process.env.DATABASE_URL,
  authToken: process.env.DATABASE_AUTH_TOKEN,
});

Next Steps

Need Help?

Released under the MIT License.