c15t
/
C15T Logo
Select a version
Frameworks
Welcome to c15t Docs
Introduction to Consent Management (c15t)
AI Tools Integrations
OSS
Contributing to c15t.com
License
Building Privacy Tools in the Open
Legal
Cookie Policy
Privacy Policy
C15T Logo
HomeFrontendIntegrationsSelf HostChangelog
xbskydiscordgithub1.4k
c15t
/
C15T Logo
Select a version
Frameworks
Welcome to c15t Docs
Introduction to Consent Management (c15t)
AI Tools Integrations
OSS
Contributing to c15t.com
License
Building Privacy Tools in the Open
Legal
Cookie Policy
Privacy Policy
home-2Docs
chevron-rightSelf-host
chevron-rightV1
chevron-rightAdapters
chevron-rightDrizzle

Drizzle Adapter

The Drizzle adapter integrates c15t Backend with Drizzle ORM, a lightweight, type-safe SQL query builder with schema declaration.

Deprecated Feature

@c15t/backend v1 did not deliver the flexibility we wanted and fell short of our standards. It is now deprecated as we work on a full rewrite, with v2 entering canary soon. This does not affect consent.io deployments, which remain stable.

Installation

Install Drizzle ORM and the appropriate database driver:

# For PostgreSQL
npm install drizzle-orm pg @types/pg
# For MySQL
npm install drizzle-orm mysql2 @types/mysql2
# For SQLite
npm install drizzle-orm better-sqlite3 @types/better-sqlite3

Configuration

  1. Define your schema using Drizzle's schema builder:
// schema.ts
import { pgTable, uuid, text, timestamp, json, boolean } from 'drizzle-orm/pg-core';

export const subject = pgTable("subject", {
	id: text("id").primaryKey(),
	isIdentified: boolean("is_identified").notNull(),
	externalId: text("external_id"),
	identityProvider: text("identity_provider"),
	lastIpAddress: text("last_ip_address"),
	createdAt: timestamp("created_at").notNull(),
	updatedAt: timestamp("updated_at").notNull(),
	subjectTimezone: text("subject_timezone"),
});

export const consentPurpose = pgTable("consent_purpose", {
	id: text("id").primaryKey(),
	code: text("code").notNull(),
	name: text("name").notNull(),
	description: text("description").notNull(),
	isEssential: boolean("is_essential").notNull(),
	dataCategory: text("data_category"),
	legalBasis: text("legal_basis"),
	isActive: boolean("is_active").notNull(),
	createdAt: timestamp("created_at").notNull(),
	updatedAt: timestamp("updated_at").notNull(),
});

export const consentPolicy = pgTable("consent_policy", {
	id: text("id").primaryKey(),
	version: text("version").notNull(),
	type: text("type").notNull(),
	name: text("name").notNull(),
	effectiveDate: timestamp("effective_date").notNull(),
	expirationDate: timestamp("expiration_date"),
	content: text("content").notNull(),
	contentHash: text("content_hash").notNull(),
	isActive: boolean("is_active").notNull(),
	createdAt: timestamp("created_at").notNull(),
});

export const domain = pgTable("domain", {
	id: text("id").primaryKey(),
	name: text("name").notNull().unique(),
	description: text("description"),
	allowedOrigins: json("allowed_origins"),
	isVerified: boolean("is_verified").notNull(),
	isActive: boolean("is_active").notNull(),
	createdAt: timestamp("created_at").notNull(),
	updatedAt: timestamp("updated_at"),
});

export const consent = pgTable("consent", {
	id: text("id").primaryKey(),
	subjectId: text("subject_id")
		.notNull()
		.references(() => subject.id, { onDelete: "cascade" }),
	domainId: text("domain_id")
		.notNull()
		.references(() => domain.id, { onDelete: "cascade" }),
	purposeIds: json("purpose_ids"),
	metadata: json("metadata"),
	policyId: text("policy_id").references(() => consentPolicy.id, {
		onDelete: "cascade",
	}),
	ipAddress: text("ip_address"),
	userAgent: text("user_agent"),
	status: text("status").notNull(),
	withdrawalReason: text("withdrawal_reason"),
	givenAt: timestamp("given_at").notNull(),
	validUntil: timestamp("valid_until"),
	isActive: boolean("is_active").notNull(),
});

export const consentRecord = pgTable("consent_record", {
	id: text("id").primaryKey(),
	subjectId: text("subject_id")
		.notNull()
		.references(() => subject.id, { onDelete: "cascade" }),
	consentId: text("consent_id").references(() => consent.id, {
		onDelete: "cascade",
	}),
	actionType: text("action_type").notNull(),
	details: json("details"),
	createdAt: timestamp("created_at").notNull(),
});

export const auditLog = pgTable("audit_log", {
	id: text("id").primaryKey(),
	entityType: text("entity_type").notNull(),
	entityId: text("entity_id").notNull(),
	actionType: text("action_type").notNull(),
	subjectId: text("subject_id").references(() => subject.id, {
		onDelete: "cascade",
	}),
	ipAddress: text("ip_address"),
	userAgent: text("user_agent"),
	changes: json("changes"),
	metadata: json("metadata"),
	createdAt: timestamp("created_at").notNull(),
	eventTimezone: text("event_timezone").notNull(),
});
  1. Configure the c15t instance with the Drizzle adapter:
import { c15tInstance } from '@c15t/backend';
import { drizzleAdapter } from '@c15t/backend/db/adapters/drizzle';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as tables from "@/schema";

// Create a PostgreSQL connection
const pool = new Pool({
	connectionString: process.env.DATABASE_URL,
});

// Initialize Drizzle with the connection
const db = drizzle(pool);

// Create the c15t instance
const instance = c15tInstance({
	baseURL: 'http://localhost:3000',
	database: drizzleAdapter(
		db as unknown as { [p: string]: unknown },
		{
			provider: 'pg',
			schema: {
				...tables,
			},
		},
	),
});

MySQL Configuration

import { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise';

const connection = await mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'c15t'
});

const db = drizzle(connection);

const instance = c15tInstance({
  database: drizzleAdapter({ client: db }),
});

SQLite Configuration

import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';

const sqlite = new Database('database.db');
const db = drizzle(sqlite);

const instance = c15tInstance({
  database: drizzleAdapter({ client: db }),
});

Usage Examples

Basic CRUD Operations

// Create a new record
const user = await instance.database.create('users', {
  name: 'John Doe',
  email: 'john@example.com'
});

// Find records
const users = await instance.database.find('users', {
  where: { email: 'john@example.com' },
  orderBy: { createdAt: 'desc' },
  limit: 10
});

// Update a record
const updatedUser = await instance.database.update(
  'users',
  { where: { id: user.id } },
  { name: 'John Smith' }
);

// Delete a record
await instance.database.delete('users', { where: { id: user.id } });

Transactions

await instance.database.transaction(async (trx) => {
  const user = await trx.create('users', {
    name: 'Alice',
    email: 'alice@example.com'
  });
  
  await trx.create('profiles', {
    userId: user.id,
    bio: 'Software engineer'
  });
});

Migrations

Use Drizzle Kit for schema migrations:

npm install -D drizzle-kit

# Generate a migration
npx drizzle-kit generate:pg

# Apply migrations
npx drizzle-kit push:pg

Type Safety

The Drizzle adapter provides excellent type safety:

import { users } from './schema';
import { InferModel } from 'drizzle-orm';

// Infer types from your schema
type User = InferModel<typeof users>;

// Type-safe operations
const users = await instance.database.find<User>('users', {
  where: { email: 'john@example.com' }
});

Best Practices

  • Define schema using Drizzle's builders - Leverage type safety and schema validation
  • Use prepared statements - Drizzle uses prepared statements for all queries
  • Implement connection pooling - Configure appropriate pool sizes for production
  • Use migrations for schema changes - Manage schema changes with Drizzle Kit

Limitations

  • Some complex queries may require direct Drizzle client usage
  • Table names must match schema definitions

Related Resources

  • Drizzle ORM Documentation
  • Database Adapter Interface
  • Core Concepts
C15T Logo
Leverage native React components for seamless integration and high performance in a robust Consent Management solution that empowers your development team while prioritizing privacy and compliance.
Product
  • Documentation
  • Components
Company
  • GitHub
  • Contact
Legal
  • Privacy Policy
  • Cookie Policy
c15t