Defining Tables with the Table DSL
Define typed table schemas with columns, primary keys, and foreign keys.
Defining Tables with the Table DSL is a free Kotlin Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Exposed Table Objects
In Exposed, database tables are represented as Kotlin singleton objects that extend Table (Query DSL) or IntIdTable/LongIdTable/UUIDTable (DAO layer). Column definitions are Kotlin properties.
A Basic Table Definition
Extend Table and define columns using the built-in column factory functions:
import org.jetbrains.exposed.sql.Table
object Users : Table("users") {
val id = long("id").autoIncrement()
val name = varchar("name", 100)
val email = varchar("email", 255).uniqueIndex()
val age = integer("age").nullable()
override val primaryKey = PrimaryKey(id)
}Column Types
Exposed provides factory functions for common SQL types:
integer("col"),long("col"),short("col")varchar("col", length),text("col")bool("col"),double("col"),decimal("col", precision, scale)datetime("col"),date("col"),timestamp("col")blob("col"),uuid("col"),binary("col", length)
Nullable Columns
Call .nullable() on any column to allow NULL values. The Kotlin type becomes nullable (String? instead of String):
object Products : Table("products") {
val id = long("id").autoIncrement()
val name = varchar("name", 200)
val description = text("description").nullable()
override val primaryKey = PrimaryKey(id)
}Default Values
Use .default(value) for a Kotlin-side default (applied before insert) or .defaultExpression(expr) for a database-side SQL default:
object Orders : Table("orders") {
val id = long("id").autoIncrement()
val status = varchar("status", 20).default("PENDING")
val createdAt = datetime("created_at")
.defaultExpression(CurrentDateTime)
override val primaryKey = PrimaryKey(id)
}Indexes and Unique Constraints
Call .index() for a regular index or .uniqueIndex() for a unique index directly on a column. For composite indexes, use the index() function on the Table:
object Articles : Table("articles") {
val id = long("id").autoIncrement()
val title = varchar("title", 300).index()
val slug = varchar("slug", 300).uniqueIndex()
override val primaryKey = PrimaryKey(id)
}Foreign Keys
Use .references() to declare a foreign key constraint between columns. Pass the referenced column and optionally the onDelete and onUpdate actions:
object Posts : Table("posts") {
val id = long("id").autoIncrement()
val authorId = long("author_id").references(Users.id, onDelete = ReferenceOption.CASCADE)
val title = varchar("title", 300)
override val primaryKey = PrimaryKey(id)
}IdTable Variants
For the DAO layer, use typed ID tables instead of plain Table:
IntIdTable("name")—Intauto-increment primary key namedidLongIdTable("name")—Longauto-increment primary keyUUIDTable("name")— UUID primary key
object Users : LongIdTable("users") {
val name = varchar("name", 100)
val email = varchar("email", 255).uniqueIndex()
}Composite Primary Keys
Define a composite primary key by passing multiple columns to PrimaryKey():
object UserRoles : Table("user_roles") {
val userId = long("user_id").references(Users.id)
val roleId = long("role_id").references(Roles.id)
override val primaryKey = PrimaryKey(userId, roleId)
}Enum Columns
Store Kotlin enum values as strings or ordinals using enumerationByName() or enumeration():
enum class Status { ACTIVE, INACTIVE, BANNED }
object Users : LongIdTable("users") {
val name = varchar("name", 100)
val status = enumerationByName<Status>("status", 20)
}Applying Schema Changes
Call SchemaUtils.createMissingTablesAndColumns() to add missing tables and columns without dropping existing data — a safe migration for development. For production, use a migration tool like Flyway or Liquibase.
transaction {
SchemaUtils.createMissingTablesAndColumns(Users, Posts, Orders)
}Quick Check
How do you declare a foreign key from Posts.authorId to Users.id in Exposed's Table DSL?
Recap: Defining Tables with the Table DSL
Key takeaways:
- Tables are
objects extendingTable,LongIdTable, etc. - Columns:
varchar,integer,long,bool,datetime,uuid, etc. .nullable(),.default(),.uniqueIndex(),.references()modify columns- Composite primary keys:
PrimaryKey(col1, col2) - Apply schema with
SchemaUtils.create()orcreateMissingTablesAndColumns()
Frequently asked questions
Is the “Defining Tables with the Table DSL” lesson free?
Yes — the full text of “Defining Tables with the Table DSL” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.
What will I learn in “Defining Tables with the Table DSL”?
Define typed table schemas with columns, primary keys, and foreign keys. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Kotlin Academy?
No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Defining Tables with the Table DSL” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Kotlin Academy lesson?
Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.