Drift KV Configuration Guide

This guide provides a comprehensive overview of Drift KV's configuration options and how to use them effectively.

Configuration Overview

The Drift KV configuration is passed to the Drift constructor and consists of several key sections:

interface DriftConfig<
  TEntities extends Record<string, DriftEntity<any, any, any>> = {},
  TQueues extends Record<string, DriftQueue<any, any, any, any, any>> = {}
> {
  client: Kv;
  schemas: DriftSchemas<TEntities, TQueues>;
  hooks?: DriftHooks<TEntities, TQueues>;
}

Database Configuration

Client Setup

// Basic client setup
const client = await Deno.openKv();

const drift = new Drift({
  client,
  // Additional configuration...
});

Connection Options

The client connection can be customized with various options:

// Default client
const kv = await Deno.openKv();

// KV Connect URL
const kv = await openKv("<KV Connect URL>");

// In-memory database
const kv = await openKv(":memory:");

// Custom database path (SQLite)
const kv = await openKv("./my-database.sqlite");

Schema Configuration

Entity Schemas

Define your entity schemas using Zod for type safety and validation:

const drift = new Drift({
  schemas: {
    entities: {
      user: (drift) => new DriftEntity(drift, {
        name: "user",
        schema: (z) => {
          name: z.string().min(3),
          email: z.string().email(),
          role: z.enum(["user", "admin"]).default("user"),
        },
        options: {
          timestamps: true
        },
      }),
    },
  },
});

Queue Schemas

Define queue schemas for background processing:

const drift = new Drift({
  schemas: {
    queues: {
      emailQueue: (drift) => new DriftQueue(drift, {
        name: "email",
        schema: z.object({
          to: z.string().email(),
          subject: z.string(),
          body: z.string(),
        }),
        handler: async (data) => {
          // Queue processing logic
        },
      }),
    },
  },
});

Lifecycle Hooks

Available Hooks

Drift KV provides various hooks for different lifecycle events:

interface DriftHooks {
  /** Called when database connection is made. */
  onConnect?: (client: Kv) => Promise<void>;

  /** Called when an error occurs. */
  onError?: (error: Error) => Promise<void>;

  /** Called before executing a query. */
  beforeQuery?: (query: DriftQueryArgs<TEntities[keyof TEntities]>) => Promise<void>;

  /** Called during query execution. */
  onQuery?: (query: DriftQueryArgs<TEntities[keyof TEntities]>) => Promise<void>;

  /** Called after query execution. */
  afterQuery?: (result: QueryResponse<TEntities[keyof TEntities]>) => Promise<void>;

  /** Called before a job is scheduled */
  onJobBeforeSchedule?: (job: DriftQueueJob<TQueues[keyof TQueues]>) => Promise<void>;

  /** Called after a job is scheduled */
  onJobSchedule?: (job: DriftQueueJob<TQueues[keyof TQueues]>) => Promise<void>;

  /** Called when a job encounters an error */
  onJobError?: (error: Error, job: DriftQueueJob<TQueues[keyof TQueues]>) => Promise<void>;
  
  /** Called after a job is processed */
  onJobEnd?: (result: DriftQueueJob<TQueues[keyof TQueues]>) => Promise<void>;
}

Implementing Hooks

Example of implementing various hooks:

const drift = new Drift({
  client,
  schemas: {
    entities: {
      user: (drift) => new DriftEntity(drift, {
        name: "user",
        schema: (z) => {
          name: z.string().min(3),
          email: z.string().email(),
          role: z.enum(["user", "admin"]).default("user"),
        },
      }),
    },
  },
  hooks: {
    onConnect: async (client) => {
      console.log("Database connected successfully");
      // Initialize post-connection resources
    },

    onError: async (error) => {
      console.error("Database error:", error);
      // Log error to monitoring service
    },

    beforeQuery: async (query) => {
      console.log("Executing query:", query);
      // Validate or modify query
    },

    afterQuery: async (result) => {
      console.log("Query result:", result);
      // Process or transform results
    },

    onJobBeforeSchedule: async (job) => {
      console.log("Scheduling job:", job);
      // Prepare job context
    },

    onJobSchedule: async (job) => {
      console.log("Job scheduled:", job);
      // Initialize post-scheduling resources
    },

    onJobError: async (error, job) => {
      console.error("Job error:", error, job);
      // Log error to monitoring service
    },

    onJobEnd: async (result) => {
      console.log("Job result:", result);
      // Process or transform results
    },
  },
});

Advanced Configuration

Entity Options

Configure individual entities with specific options:

new DriftEntity(drift, {
  name: "user",
  schema: (z) => {
    name: z.string().min(3),
    email: z.string().email(),
    role: z.enum(["user", "admin"]).default("user"),
  },
  options: {
    timestamps: true,
  },
  hooks: {
    beforeCreate: async (data) => {
      // Pre-creation logic
    },
    afterUpdate: async (result) => {
      // Post-update logic
    },
  },
});

Queue Options

Configure job queues with specific options:

new DriftQueue(drift, {
  name: "emailQueue",
  schema: emailSchema,
  hooks: {
    onError: async (error, data) => {
      // Error handling logic
    },
  },
});

Environment-Specific Configuration

Development Configuration

const isDev = process.env.NODE_ENV === "development";

const getHooks = () => {
  if (isDev) {
    return {
      beforeQuery: async (query) => {
        console.log("DEV MODE - Query:", query);
      },
    };
  }
};

const drift = new Drift({
  // rest of config,
  hooks: getHooks(),
});

Production Configuration

const isProd = process.env.NODE_ENV === "production";

const drift = new Drift({
  // rest of config,
  hooks: {
    onError: async (error) => {
      // Send to error monitoring service
      await sendToErrorMonitoring(error);
    },
    afterQuery: isProd ? async (result) => {
      // Performance monitoring in production
      await measureQueryPerformance(result);
    } : undefined,
  },
});

Best Practices

  1. Configuration Organization

    • Keep configuration modular and environment-specific
    • Use environment variables for sensitive information
    • Implement proper error handling in hooks
  2. Schema Design

    • Use strict types with Zod
    • Implement proper validation
    • Consider indexing strategy
  3. Hook Implementation

    • Keep hooks focused and lightweight
    • Implement proper error handling
    • Consider performance impact

Next Steps

Built for Deno KV

Discover Drift KV: The Ultimate Type-Safe ORM for Deno KV

Experience a powerful ORM with real-time subscriptions, job queues, and seamless compatibility across Deno KV, Node.js, Bun.js, and Edge environments.