Type Adapters: Best Practices
See original GitHub issueI’m a bit unsure on the proper use and best practices of type adapters (and custom types in general).
I noticed that custom types can be supported by implementing transformValueFromDB
and transformValueToDB
directly in the DBConnection
class. On the other hand, I can define my own type adapters. Which approach should be used in which situation?
Furthermore, I’m unsure how to deal with the type
and next
parameters when writing custom type adapters.
Let’s say I want to store RGB colors as 3-byte blobs in the (SQLite) database:
export class RgbColorTypeAdapter implements TypeAdapter {
public transformValueFromDB(
value: unknown,
type: string,
next: DefaultTypeAdapter,
): RgbColor {
if (value instanceof Uint8Array && value.length == 3) {
return {r: value[0], g: value[1], b: value[2]};
}
throw new Error(`Cannot decode database value ${value} (type ${typeof value}) as RgbColor`);
}
public transformValueToDB(
value: RgbColor,
type: string,
next: DefaultTypeAdapter,
): Uint8Array {
if (isRgbColor(value)) {
return Uint8Array.of(value.r, value.g, value.b);
}
throw new Error(`Cannot encode value ${value} for database`);
}
}
This works, but if I use it like this:
public color = this.optionalColumn('cachedColor', 'custom', 'RgbColor', new RgbColorTypeAdapter());
…then reading a null value fails. I assume this is because I need to call next
before or after my custom code (probably depending on the from/to direction).
What are the best practices in this case?
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (5 by maintainers)
Top GitHub Comments
Added a section in the documentation explaining it at https://ts-sql-query.readthedocs.io/en/latest/column-types/
Type adapters allow you to transform values when you send it to the database
transformValueToDB
and, when you receive it from the databasetransformValueFromDB
.The one defined at the
DBConnection
is the default one. If you create a custom type that applies to the whole database, that is the best place. Define the custom type probably is the most common strategy, and it looks like in your case is where you must place it.The type adapter at column level is when you need to define a custom rule that only applies to that column. For example, the CustomBooleanTypeAdapter that allows you to create custom representations for a boolean (very useful on Oracle database, where there is not a boolean type).
The type adapter handles all the values sending or coming from the database, including null/undefined values; you must handle it.
The
type
param tells you what the expected type is; you must verify it and only process the value if it is the one you are applying the rule.The
next
gives you access to the default implementation; you must call if you cannot handle the type.In your case, your code will look like this:
And then you will use it as:
But, in your case is better to define at
DBConnection
level. The connection will look like this:And then you will use it as: