Separate query structure from untrusted values
An HTML or SQL DSL must handle escaping and parameterization, not merely attractive syntax. For SQL, produce a query plus bound values instead of interpolating user text into executable SQL.
data class Query(val sql: String, val arguments: List<String>)
fun lessonsByTopic(topic: String): Query = Query(
sql = "SELECT id, title FROM lessons WHERE topic = ?",
arguments = listOf(topic)
)
The database API must actually bind the arguments when executing this model. A question mark stored in a string does nothing by itself. Table and column names generally cannot be bound like values; allowlist them if the DSL permits configurable identifiers.
For HTML, escape text according to its output context and distinguish trusted markup from ordinary text. URL attributes need their own validation policies.
Exercise
Pass a topic containing quotes and SQL-like text. Confirm the SQL template stays unchanged and the complete topic becomes one argument. Design a tiny allowlist for sort columns.
Check: a fluent API is not safe merely because it is type-safe Kotlin.