[FEATURE]: Default select for custom types

Author: drepkovskyCreated Aug 21, 2023Updated Sep 15, 2026
Labelsenhancementqb/crud

Describe what you want

When creating a custom type that doesn't have a straightforward select like a postgis geometry, for example.

typescript
export type Point = {
  lat: number;
  lng: number;
};

export const pointType = customType<{ data: Point; driverData: string }>({
  dataType() {
    return 'geometry(Point,4326)';
  },
  toDriver(value: Point): string {
    return `SRID=4326;POINT(${value.lng} ${value.lat})`;
  },
  fromDriver(value: string) {
    const matches = value.match(/POINT\((?<lng>[\d.-]+) (?<lat>[\d.-]+)\)/);
    const { lat, lng } = matches?.groups ?? {};

    return { lat: parseFloat(String(lat)), lng: parseFloat(String(lng)) };
  },
});

We need to create a select function wrapper to properly select the field with this custom type.

typescript
export const selectPoint = (column: string, decoder: DriverValueMapper<any, any>) => {
  return sql<Point>`st_astext(${sql.identifier(column)})`.mapWith(decoder).as(column);
};


// then select it like this:
db.select({
   ...allOtherFields
  coords: selectPoint('coords', location.coords),
}).from(location);

That means we always need to specify this custom select when working with this table, also we are not able to use the relational query syntax because the there is no way to provide a custom sql fragment in the columns object (only true/false boolean specifying the field inclusion)

Proposal:

Add a selectFromDb option to customType factory function like so:

typescript
export const pointType = customType<{ data: Point; driverData: string }>({
  dataType() {
    return 'geometry(Point,4326)';
  },
  toDriver(value: Point): string {
    return `SRID=4326;POINT(${value.lng} ${value.lat})`;
  },
  fromDriver(value: string) {
    const matches = value.match(/POINT\((?<lng>[\d.-]+) (?<lat>[\d.-]+)\)/);
    const { lat, lng } = matches?.groups ?? {};

    return { lat: parseFloat(String(lat)), lng: parseFloat(String(lng)) };
  },

 /** this is new */
  selectFromDb(column, decoder) {
    return sql<Point>`st_astext(${sql.identifier(column)})`.mapWith(decoder).as(column);
  },
});

Now we when the field is being selected from db the selectFromDb function will be automatically called if specified for given field.

Source: drizzle-team/drizzle-orm