K
Kysely•17mo ago
elitan

Build a basic Kysely Plugin

Hi, I need some help with a basic plugin I want to build. It's an encrypt/decrypt plugin. This is high level on how I want it to work: - Before data is written to the database, encrypt it - Before data is read back, decrypt it. This should only work for string values. The trick is, not all columns should be encrypted. I'm fine defining tables/columns this will be applied to. Maybe the plugin should have a encrypt function, decrypt function, and a list of table/column pairs this is applicable for? Can anyone help me with some high level help on how to build this?
5 Replies
Igal
Igal•17mo ago
Hey 👋 Neat idea! High-level, might be slightly inaccurate in names of things: Basically, plugins have 2 methods: transformQuery that receives the syntax tree and returns it after making changes to it. transformResults that receives the query results and returns them after making changes to them. Usually you pass transformQuery's input to a transformer class that extends some default transformer and overrides specific visit methods. a visit method exists per syntax tree node type. You're correct in thinking that you'd need the table/column names from outside. with this approach its best the plugin is only used per query and not globally, because well, column names are not unique, and queries can introduce aliases. another approach would be, to inject a hint on the value "encrypt this" when passing the values to the query builder, and after encrypting, concat a "decrypt this" hint to the encrypted string. this makes for a very dumb and simple plugin, see hint, do action. but comes with slightly worse DX when querying. hinting could also be achieved by just wrapping the input strings with plain objects of a certain shape (having a certain symbol as one of the fields?).
elitan
elitanOP•17mo ago
Nice, thanks for the overview. Is there a boilerplate plugin that I can use as a starting point? I usually learn fastest with examples. Maybe a plugin that does something super simple but where you would understand the flow and setup.
koskimas
koskimas•17mo ago
This plugin is as simple as it gets https://github.com/kysely-org/kysely/tree/master/src/plugin/immediate-value The other plugins in the plugin folder might help too
elitan
elitanOP•17mo ago
Thanks, will check it out Been playing around with this now for some time and I don't think it's worth pursuing. To many cases to consider. Aliases are one. Not having the table name in the result is another. I'm starting to think that a "application level" encrypt/decrypt plugin is not the right match for Kysely @Igal How do you imagine this "hint" approach to work? I'm not following exactly what you mean here.
Igal
Igal•17mo ago
pseudo, but hope you get the idea. very naive approach but could work.
const KYSELY_ENCRYPTION_HINT = ':::KYSELY_ENCRYPTION_HINT:::'

type Encrypted = string & { __encrypyted__: true }

interface DB {
some_table: {
column_to_encrypt: ColumnType<string, Encrypted, Encrypted>
}
}

function encrypted(value: string): Encrypted {
return `${KYSELY_ENCRYPTION_HINT}${value}` as any // :::KYSELY_ENCRYPTION_HINT:::${value}
}

// insert query (can also do it in update query)

.values({ column_to_encrypt: encrypted(value) })

// plugin

transformQuery(query) {
// pass to transformer
}

transformResults(results) {
// iterate over rows and columns
if (typeof value === 'string' && value.startsWith(KYSELY_ENCRYPTION_HINT)) {
value = this.decrypt(value.substring(KYSELY_ENCRYPTION_HINT.length))
}
}

// transformer

visitValue(node) {
if (typeof value === 'string' && value.startsWith(KYSELY_ENCRYPTION_UNIT)) {
value = `${KYSELY_ENCRYPTION_HINT}${this.encrypt(value.substring(KYSELY_ENCRYPTION_HINT.length))}`
}
}
const KYSELY_ENCRYPTION_HINT = ':::KYSELY_ENCRYPTION_HINT:::'

type Encrypted = string & { __encrypyted__: true }

interface DB {
some_table: {
column_to_encrypt: ColumnType<string, Encrypted, Encrypted>
}
}

function encrypted(value: string): Encrypted {
return `${KYSELY_ENCRYPTION_HINT}${value}` as any // :::KYSELY_ENCRYPTION_HINT:::${value}
}

// insert query (can also do it in update query)

.values({ column_to_encrypt: encrypted(value) })

// plugin

transformQuery(query) {
// pass to transformer
}

transformResults(results) {
// iterate over rows and columns
if (typeof value === 'string' && value.startsWith(KYSELY_ENCRYPTION_HINT)) {
value = this.decrypt(value.substring(KYSELY_ENCRYPTION_HINT.length))
}
}

// transformer

visitValue(node) {
if (typeof value === 'string' && value.startsWith(KYSELY_ENCRYPTION_UNIT)) {
value = `${KYSELY_ENCRYPTION_HINT}${this.encrypt(value.substring(KYSELY_ENCRYPTION_HINT.length))}`
}
}
Want results from more Discord servers?
Add your server