Is there a way to avoid confusing with different foreign keys?
See original GitHub issueI have two simple tables:
CREATE TABLE agencies (
id SERIAL PRIMARY KEY
....
);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
agency_id INT NOT NULL REFERENCES agencies (id)
first_name TEXT NOT NULL,
last_name TEXT NOT NULL
);
A single agency has many users. This is pretty straightforward. So if I type:
export const getAgencyUsers = sql`
SELECT * FROM users WHERE agency_id = $agencyId
`;
Then $agencyId would be evaluated as number
for typescript, which is perfectly fine. But when the application grows, we can mistakenly pass the wrong entity id (e.g user id instead of agency id) and since typescript evaluates both user id and agency id as numbers, so we can easily get mistaken. At my work, we don’t use pgtyped, we’ve got something else. But the thing is when it comes to column keys, we treat each key as a different class. So user id would be UserId
and agency id would be AgencyId
. This way, we can never make a mistake. For example
export class UserId {
private constructor() { }
"UserId": UserId;
public static wrap(id: number): UserId {
return <any>id;
}
public static unwrap(userId: UserId): number {
return <any>userId;
}
}
so if by mistake I passed the wrong key (let’s say $agencyId
instead of $userId
, then I would get a type check error like so:
I was wondering if this is possible to achieve with pgtyped as well.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:13 (4 by maintainers)
You know, as it often happens when you get very self-confident, I just had to debug a nasty issue that originated exactly from this kind of confusion. In the code that I wrote personally, all the way. So, even if you didn’t post this (pretty convincing, by the way) comment, I just had to get back to this discussion and humbly concede that we probably do, indeed, need a more rigid way to prevent this kind of bugs.
@golergka Thanks for the tip. And still, I’ll try to draw a scenario where this can get out of hand. Take this code for example:
Looks fine, no chance that updateUserCash will pass the wrong params to the query. But what about the ones that call
updateUserCash
? This can quickly get out of hand when working with multiple devs on functions that have lots of params. Of course, we can convert the params to a single one that should be an object, but that will require us to write an undesirable boilerplate for each function (a separate or an inline interface).About backward compatibility, can’t we make a flag that it turned off by default (I hope?).
Another library that adds typescript types from pg schema that I found today is kristiandupont/kanel Which translate keys as “opaque” types:
taken from https://github.com/kristiandupont/kanel/blob/master/example/models/Actor.ts