Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions drizzle-orm/src/d1-http/driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { BatchItem, BatchResponse } from '~/batch.ts';
import { entityKind } from '~/entity.ts';
import { DefaultLogger } from '~/logger.ts';
import type { ExtractTablesWithRelations, RelationalSchemaConfig, TablesRelationalConfig } from '~/relations.ts';
import { createTableRelationsHelpers, extractTablesRelationalConfig } from '~/relations.ts';
import { BaseSQLiteDatabase } from '~/sqlite-core/db.ts';
import { SQLiteAsyncDialect } from '~/sqlite-core/dialect.ts';
import type { DrizzleConfig } from '~/utils.ts';
import { D1HttpSession } from './session.ts';

export interface D1HttpCredentials {
/** The Cloudflare account ID where the D1 database is located */
accountId: string;
/** The D1 database ID */
databaseId: string;
/** The Cloudflare API token for the account with D1:edit permissions */
token: string;
}

export interface D1HttpResult<T = unknown> {
rows?: T[];
}

export class D1HttpDatabase<TSchema extends Record<string, unknown> = Record<string, never>> extends BaseSQLiteDatabase<
'async',
D1HttpResult,
TSchema
> {
static override readonly [entityKind]: string = 'D1HttpDatabase';

/** @internal */
declare readonly session: D1HttpSession<TSchema, ExtractTablesWithRelations<TSchema>>;

async batch<U extends BatchItem<'sqlite'>, T extends Readonly<[U, ...U[]]>>(batch: T): Promise<BatchResponse<T>> {
return this.session.batch(batch) as Promise<BatchResponse<T>>;
}
}

export function drizzle<TSchema extends Record<string, unknown> = Record<string, never>>(
credentials: D1HttpCredentials,
config: DrizzleConfig<TSchema> = {}
): D1HttpDatabase<TSchema> & {
$client: D1HttpCredentials;
} {
const dialect = new SQLiteAsyncDialect({ casing: config.casing });
let logger;
if (config.logger === true) {
logger = new DefaultLogger();
} else if (config.logger !== false) {
logger = config.logger;
}

let schema: RelationalSchemaConfig<TablesRelationalConfig> | undefined;
if (config.schema) {
const tablesConfig = extractTablesRelationalConfig(config.schema, createTableRelationsHelpers);
schema = {
fullSchema: config.schema,
schema: tablesConfig.tables,
tableNamesMap: tablesConfig.tableNamesMap,
};
}

const session = new D1HttpSession(credentials, dialect, schema, {
logger,
cache: config.cache,
});
const db = new D1HttpDatabase('async', dialect, session, schema) as D1HttpDatabase<TSchema>;
(db as any).$client = credentials;
(db as any).$cache = config.cache;
if ((db as any).$cache) {
(db as any).$cache['invalidate'] = config.cache?.onMutate;
}

return db as any;
}
3 changes: 3 additions & 0 deletions drizzle-orm/src/d1-http/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './driver.ts';
export * from './migrator.ts';
export * from './session.ts';
49 changes: 49 additions & 0 deletions drizzle-orm/src/d1-http/migrator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { MigrationConfig } from '~/migrator.ts';
import { readMigrationFiles } from '~/migrator.ts';
import { sql } from '~/sql/sql.ts';
import type { D1HttpDatabase } from './driver.ts';

export async function migrate<TSchema extends Record<string, unknown>>(
db: D1HttpDatabase<TSchema>,
config: MigrationConfig
) {
const migrations = readMigrationFiles(config);
const migrationsTable = config.migrationsTable ?? '__drizzle_migrations';

const migrationTableCreate = sql`
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
id SERIAL PRIMARY KEY,
hash text NOT NULL,
created_at numeric
)
`;
await db.session.run(migrationTableCreate);

const dbMigrations = await db.values<[number, string, string]>(
sql`SELECT id, hash, created_at FROM ${sql.identifier(migrationsTable)} ORDER BY created_at DESC LIMIT 1`
);

const lastDbMigration = dbMigrations[0] ?? undefined;

const statementToBatch = [];

for (const migration of migrations) {
if (!lastDbMigration || Number(lastDbMigration[2])! < migration.folderMillis) {
for (const stmt of migration.sql) {
statementToBatch.push(db.run(sql.raw(stmt)));
}

statementToBatch.push(
db.run(
sql`INSERT INTO ${sql.identifier(migrationsTable)} ("hash", "created_at") VALUES(${sql.raw(
`'${migration.hash}'`
)}, ${sql.raw(`${migration.folderMillis}`)})`
)
);
}
}

if (statementToBatch.length > 0) {
await db.session.batch(statementToBatch);
}
}
Loading