Skip to main content

Type Signature

type Insertable<R> = DrainOuterGeneric<
  {
    [K in NonNullableInsertKeys<R>]: InsertType<R[K]>
  } & {
    [K in NullableInsertKeys<R>]?: InsertType<R[K]>
  }
>

Overview

Insertable<T> is a utility type that extracts the insert type from all columns in a table interface. It resolves the InsertType from any ColumnType definitions and creates a type representing what you can insert into the table.

Type Parameters

R
interface
required
A table interface with column definitions.

Behavior

  • Extracts the second type parameter (InsertType) from each ColumnType<SelectType, InsertType, UpdateType>
  • For regular types without ColumnType, uses the type as-is
  • Fields with nullable insert types (can be undefined or null) become optional
  • Fields with non-nullable insert types remain required
  • Excludes columns where InsertType is never

Examples

Basic Usage

import { Generated, ColumnType, Insertable } from 'kysely'

interface PersonTable {
  id: Generated<number>
  first_name: string
  last_name: string
  created_at: ColumnType<Date, string, never>
}

type InsertablePerson = Insertable<PersonTable>
// {
//   id?: number
//   first_name: string
//   last_name: string
//   created_at: string
// }
Notice:
  • id is optional (because Generated<number> allows undefined for inserts)
  • created_at is a string (not Date) for inserts
  • first_name and last_name are required strings

Using with Insert Queries

interface Database {
  person: PersonTable
}

const db = new Kysely<Database>({ /* ... */ })

// Type-safe insert
const newPerson: InsertablePerson = {
  first_name: 'John',
  last_name: 'Doe',
  created_at: '2024-01-01T00:00:00Z'
}

await db
  .insertInto('person')
  .values(newPerson)
  .execute()

// Or inline
await db
  .insertInto('person')
  .values({
    first_name: 'Jane',
    last_name: 'Smith',
    created_at: new Date().toISOString()
  })
  .execute()

With GeneratedAlways Columns

interface ProductTable {
  id: GeneratedAlways<number>
  name: string
  price: number
  created_at: Generated<Date>
}

type InsertableProduct = Insertable<ProductTable>
// {
//   name: string
//   price: number
//   created_at?: Date
// }
// Note: id is excluded because GeneratedAlways has InsertType = never

Optional vs Required Fields

interface UserTable {
  // Required fields (non-nullable insert type)
  username: string
  email: string
  
  // Optional fields (nullable insert type)
  id: Generated<number>
  middle_name: string | null
  bio: string | undefined
  avatar_url?: string
}

type InsertableUser = Insertable<UserTable>
// {
//   username: string          // required
//   email: string             // required
//   id?: number               // optional (Generated)
//   middle_name?: string | null    // optional (nullable)
//   bio?: string | undefined       // optional (nullable)
//   avatar_url?: string            // optional (optional in table)
// }

Different Types for Insert

interface EventTable {
  id: Generated<number>
  name: string
  // Accept string for insert, but return Date for select
  event_date: ColumnType<Date, string, string>
  // JSON column: accept string, return object
  metadata: ColumnType<{ tags: string[] }, string, string>
  // Read-only column
  created_at: ColumnType<Date, never, never>
}

type InsertableEvent = Insertable<EventTable>
// {
//   id?: number
//   name: string
//   event_date: string
//   metadata: string
//   // created_at excluded (InsertType is never)
// }

await db
  .insertInto('event')
  .values({
    name: 'Conference',
    event_date: '2024-06-15',
    metadata: JSON.stringify({ tags: ['tech', 'ai'] })
  })
  .execute()

Common Patterns

Function Parameters

type InsertablePerson = Insertable<PersonTable>

async function createPerson(person: InsertablePerson) {
  return await db
    .insertInto('person')
    .values(person)
    .returningAll()
    .executeTakeFirstOrThrow()
}

// Usage
const person = await createPerson({
  first_name: 'John',
  last_name: 'Doe',
  created_at: new Date().toISOString()
})

Bulk Inserts

type InsertablePerson = Insertable<PersonTable>

const people: InsertablePerson[] = [
  { first_name: 'John', last_name: 'Doe', created_at: '2024-01-01' },
  { first_name: 'Jane', last_name: 'Smith', created_at: '2024-01-02' },
]

await db
  .insertInto('person')
  .values(people)
  .execute()

Combining with Selectable

import { Selectable, Insertable } from 'kysely'

type Person = Selectable<PersonTable>
type NewPerson = Insertable<PersonTable>

async function createPerson(newPerson: NewPerson): Promise<Person> {
  return await db
    .insertInto('person')
    .values(newPerson)
    .returningAll()
    .executeTakeFirstOrThrow()
}

Partial Insertable with Pick

// Only require certain fields
type PersonNameOnly = Pick<InsertablePerson, 'first_name' | 'last_name'>

function validatePersonName(name: PersonNameOnly) {
  if (!name.first_name || !name.last_name) {
    throw new Error('Name is required')
  }
}

Source

View source on GitHub

Build docs developers (and LLMs) love