kysely
kysely copied to clipboard
add `ReadonlyKysely<DB>` helper.
as requested by @ethanresnick on discord.
Real-world use case: knowing you're exposing a Kysely instance that's connecting to a read replica, or uses a user that only has read permissions, you want to catch data mutating queries during compilation.
This obviously doesn't provide safety for raw queries. We could omit getExecutor so it can't be used as executor provider for sql template tag.
Downside is we'll have to maintain it, keep it aligned with changes to Kysely, QueryCreator, Transaction. Pick ensures it keeps its promise, even if it falls behind.
- [x] implement
- [ ] typings tests
Wow, thanks @igalklebanov! I'm impressed you got this up so fast, and really appreciate it!
Now that you've written the implementation, I'll be curious to hear what you and @koskimas think about the value vs. maintenance effort tradeoff.
I don't know about this 🤔 This will easily get out of sync with the RW version and there's no way to create tests that make sure it doesn't. Each time we add a new method to the RW versions we need to remember to add a read-only version (if applicable). Expecially those ReadonlyCommonTableExpression types seem like a maintenance nightmare.
Adding some context, we did something similar a week ago in our project to protect an instance of kysely over a slave connection database
import { Kysely } from 'kysely';
import { Database } from './database';
export class ReadonlyDatabaseConnection extends Kysely<Database> {
/**
* Don't use me, please. I'm a readonly database connection
*/
declare replaceInto: never;
/**
* Don't use me, please. I'm a readonly database connection
*/
declare insertInto: never;
/**
* Don't use me, please. I'm a readonly database connection
*/
declare deleteFrom: never;
/**
* Don't use me, please. I'm a readonly database connection
*/
declare updateTable: never;
}
Don't cover every situation (using with* & other methods) but it's mostly perfect to avoid certain actions. So it was a surprise to see this PR!
This will easily get out of sync with the RW version and there's no way to create tests that make sure it doesn't.
What about make it a class/subclass? ReadWriteKysely extends ReadOnlyKysely I don't know if it's technically possible or it's simply a bad idea... 😆
Good work @koskimas & @igalklebanov, I really enjoy using Kysely 👍🏻
@koskimas
I don't know about this
Same.. the overhead for future contributions/reviews makes this really questionable. Thought the implementation would be not so hard (right now) and wanted to make this idea more presentable for this discussion.
Maybe this should be a separate package, with tight peer dependency? could even align version numbers.. would be a hell to maintain, but won't affect Kysely.
What about make it a class/subclass?
Chaining API + generics + subclassing is kinda difficult/impossible in typescript:
class A<T> {
f<T2>(a: T2) A<T2> {
return new A()
}
}
class B<T> extends A<T> {
}
const b1 = new B<number>()
const b2 = b1.f("hello") // Type is A<string> and not B<string>.
There's no way to say "this type with T2 as generic argument" in typescript. What we'd want here is something like
class A<T> {
f<T2>(a: T2) this<T2> {
return new this.constructor()
}
}
but there's no way to do this<T2> currently. We need to redeclare all methods in B
class B<T> extends A<T> {
f<T2>(a: T2) B<T2> {
return new B()
}
}
Same.. the overhead for future contributions/reviews makes this really questionable. Thought the implementation would be not so hard (right now) and wanted to make this idea more presentable for this discussion.
This could still be worth it if enough people find this useful. I simply don't know. I've never found myself wanting something like this.
Let's leave this open for now. This PR shouldn't rot too fast since it's a separate set of interfaces.
This could still be worth it if enough people find this useful. I simply don't know. I've never found myself wanting something like this.
If things like this gain more traction, we might see more interest from the edge function crowd.