I'm just wondering if it's possible to intercept previous methods in a class chain, i have these classes
class And {
client;
table;
condition;
constructor(client, table, condition) {
this.client = client;
this.table = table;
this.condition = condition;
}
and(anotherCondition) {
return this.client.query(
`SELECT * FROM "${this.table}" WHERE ${this.condition} AND ${anotherCondition};`
);
}
}
class Where {
client;
table;
constructor(client, table) {
this.client = client;
this.table = table;
}
where(condition) {
return this.client.query(
`SELECT * FROM "${this.table}" WHERE ${condition};`
);
}
}
class Select {
client;
constructor(client) {
this.client = client;
}
from(table) {
return this.client.query(`SELECT * FROM "${table}";`);
}
}
class Database {
client;
constructor(client) {
this.client = client;
}
select() {
return new Select(this.client);
}
}
would it be possible to do something like this?
const db = new Database(client);
await db.select().from(users);
//> return all users
await db.select().from(users).where("id = 1");
//> intercept from() method and return all users with a where condition
await db.select().from(users).where("id = 1").and("email like '%gmail%'");
//> intercept previous two methods and run query with additional and condition
await db.select().from(users).where("id = 1").and("email like '%gmail%'").and("type = 'END_USER'");
//> similar with infinite `and` chains
what i want is being able to chain methods but it also depends on what methods are chained and return the result according to that.
i've read about Proxy and Reflect but i couldn't make any sense from it, any help would be much appreciated!
Since we are using promises here, it's easy just to postpone our decision to check whether our promise was chained and act accordingly. I think some generic wrapper is possible to avoid manual work here, but yoga is waiting me unfortunately:
class Where {
client;
table;
constructor(client, table) {
this.client = client;
this.table = table;
}
where(condition) {
return this.client.query(
`SELECT * FROM "${this.table}" WHERE ${condition};`
);
}
}
class Select {
client;
constructor(client) {
this.client = client;
}
from(table) {
let chained = false;
const promise = new Promise(resolve => {
// postpone into a microtask
queueMicrotask(() => resolve(chained || this.client.query(`SELECT * FROM "${table}";`)));
});
promise.where = condition => {
chained = true;
return new Where(this.client, table).where(condition)
};
return promise;
}
}
class Database {
client;
constructor(client) {
this.client = client;
}
select() {
return new Select(this.client);
}
}
const client = {
query(query) {
console.log('executing query:', query);
return new Promise(r => setTimeout(() => r(query.includes('id = 1') ? { fname: 'Alexander', lname: 'Nenashev' } : [{ fname: 'Alexander', lname: 'Nenashev' }]), 1000));
}
}
<script type="module">
const db = new Database(client);
console.log(await db.select().from('users'));
console.log(await db.select().from('users').where('id = 1'));
</script>