Skip to main content
Version: Latest - 1.0.0-alpha.25

Insert

Use db.insert() to insert rows into a table. Durcno provides full type safety, ensuring you provide all required columns and use correct types.

Methods

Builder methods (InsertBuilder)

MethodDescription
.values()Supply row(s) to insert

Query methods (InsertQuery)

MethodDescription
.onConflict()Start an ON CONFLICT clause for upsert-style handling
.returning(cols)Specify columns to return after insert (use "*" for all columns)

Basic Usage

Insert a Single Row

import { db } from "./db/index.ts";
import { Users } from "./db/schema.ts";

await db.insert(Users).values({
username: "john_doe",
email: "john@example.com",
type: "user",
});

Insert Multiple Rows

Pass an array to .values() to insert multiple rows:

await db.insert(Users).values([
{ username: "john_doe", email: "john@example.com", type: "user" },
{ username: "jane_doe", email: "jane@example.com", type: "admin" },
]);

Upserts with ON CONFLICT

Use .onConflict() to build PostgreSQL ON CONFLICT clauses for insert operations. You can target one or more columns, or omit the arguments to apply the clause to any conflict.

Skip conflicting inserts with .doNothing()

await db
.insert(Users)
.values({
username: "john_doe",
email: "john@example.com",
type: "user",
})
.onConflict(Users.username)
.doNothing();

Update conflicting rows with .doUpdateSet()

Use .doUpdateSet() to update existing rows when a conflict occurs. The callback receives an excluded object that references the values from the incoming insert row.

await db
.insert(Users)
.values({
username: "john_doe",
email: "updated@example.com",
type: "user",
})
.onConflict(Users.username)
.doUpdateSet(({ excluded }) => ({
email: excluded.email,
}));

You can also provide an optional predicate to make the update conditional:

import { gt } from "durcno";

await db
.insert(Users)
.values({
username: "john_doe",
score: 100,
})
.onConflict(Users.username)
.doUpdateSet(
({ excluded }) => ({ score: excluded.score }),
({ excluded }) => gt(excluded.score, Users.score),
);

Required vs Optional Columns

Durcno automatically determines which columns are required based on your schema:

Column TypeInsert Behavior
notNull without defaultRequired - must be provided
notNull with defaultOptional - uses default if not provided
Nullable (no notNull)Optional - defaults to null
Primary key (pk())Optional - auto-generated
Generated Always (generatedAlways())Non-insertable - excluded from insert keys (never)
Generated By Default (generatedByDefault())Optional - uses database identity unless provided

Example Schema

const Users = table("public", "users", {
id: pk(), // Optional (auto-generated)
username: varchar({ length: 50, notNull }), // Required
email: varchar({ length: 255 }), // Optional (nullable)
type: UserRole.enumed({ notNull }), // Required
createdAt: timestamp({ notNull }).default(now()), // Optional (has default)
});

Corresponding Insert

// Only username and type are required
await db.insert(Users).values({
username: "john_doe", // Required
type: "user", // Required
// id: auto-generated
// email: defaults to null
// createdAt: defaults to now()
});

// You can optionally provide other columns
await db.insert(Users).values({
username: "jane_doe",
type: "admin",
email: "jane@example.com", // Optional, but provided
});

Returning Inserted Data

Use .returning() to get data back from inserted rows:

// Return specific columns
const inserted = await db
.insert(Users)
.values({ username: "john_doe", type: "user" })
.returning({ id: true, username: true });
// Type: { id: bigint; username: string }[]

// Return all columns except some
const inserted = await db
.insert(Users)
.values({ username: "john_doe", type: "user" })
.returning({ email: false });
// Type: { id: bigint; username: string; type: "admin" | "user"; createdAt: Date }[]

// Return all columns using the wildcard
const inserted = await db
.insert(Users)
.values({ username: "john_doe", type: "user" })
.returning("*");
// Type: { id: bigint; username: string; email: string | null; type: "admin" | "user"; createdAt: Date }[]

Without Returning

Without .returning(), the insert returns null:

const result = await db
.insert(Users)
.values({ username: "john_doe", type: "user" });
// Type: null

Auto-generated Values with .$insertFn()

Columns with .$insertFn() automatically generate values during insert when not explicitly provided:

// Schema with `.$insertFn()`
const Posts = table("public", "posts", {
id: pk(),
title: varchar({ length: 200, notNull }),
createdAt: timestamp({ notNull }).$insertFn(() => new Date()),
});

// createdAt is optional - insertFn generates it
await db.insert(Posts).values({
title: "My Post",
// createdAt will be auto-generated
});

// You can still override with an explicit value
await db.insert(Posts).values({
title: "My Post",
createdAt: new Date("2024-01-01"), // Override insertFn
});
tip

See Dynamic Value Generation for more details on .$insertFn() and .$updateFn().

Using SQL Expressions

You can use sql() for raw SQL expressions in insert values:

import { sql } from "durcno";

await db.insert(Users).values({
username: "john_doe",
type: "user",
createdAt: sql`NOW() - INTERVAL '1 day'`,
});

Type Safety

Durcno provides compile-time validation:

// ✅ Valid - all required fields provided
await db.insert(Users).values({
username: "john",
type: "user",
});

// ❌ TypeScript Error - missing required field "type"
await db.insert(Users).values({
username: "john",
});

// ❌ TypeScript Error - invalid type value
await db.insert(Users).values({
username: "john",
type: "superadmin", // Not in enum
});