Campos BIT en MySQL desde Node.js

Campos BIT en MySQL desde Node.js

Una de las formas más comunes de persistir el estado de un registro es mediante un campo que nos indique si se encuentra activo o no. Hace muchos años empecé utilizando un campo de tipo entero y únicamente me preocupaba por setearlo a un valor 0 para falso y un valor 1 para verdadero. Posteriormente delimité dicho campo a una longitud de 1 byte para optimizar espacio en disco, para luego descubrir que existía el tipo de dato TINYINT, el cual también puede delimitarse a una longitud de 1 byte para, nuevamente, optimizar espacio en disco.

No fue sino hasta mucho tiempo después que descubrí el peculiar tipo de dato BIT en MySQL. Lo declaramos en manera muy resumida de la siguiente manera:

CREATE TABLE `nombreDeLaTabla` (
    --
    `nombreDelCampo` BIT(1) DEFAULT b'1'
    --
)ENGINE=InnoDB;

Al definir el campo con un valor por defecto de b'1' estamos asegurándonos que nunca se creen registros con dicho campo nulo y de paso cualquier registro nuevo tendrá automáticamente el valor verdadero como su valor inicial.

Hasta acá todo bonito. Sin embargo hay ciertas implicaciones que se hicieron bastante evidentes al momento de exponer un campo de tipo BIT mediante una REST API en un servicio de Node.js. Al examinar el campo nos damos cuenta que en vez de venir un 1 ó 0, o un true o false, nuestro servicio devuelve algo como esto:

Nuestro campo está siendo retornado como un búfer en vez de un simple 1 ó 0, o bien un true o false

Soluciones hay muchas, pero algunas son más complicadas y trabajosas que otras. En situaciones donde es posible y tiene sentido construir una vista para exponer dicho campo, podemos sencillamente hacer algo así:

SELECT
    -- Otros campos
    IF(`nombreDelCampo` = b'1', 1, 0) AS `nombreDelCampo`
-- Resto de nuestra consulta

Utilizando el driver de MySQL en Node.js para corregir cualqueir campo de tipo BIT

Cabe aclarar que acá únicamente estamos transformando la presentación de cualquier campo que cumpla las condiciones. En ningún momento se reemplaza o transforma la información original en la base de datos.

En nuestra definición de la conexión mediante el driver de MySQL usualmente tenemos lo siguiente:

const db = require('mysql')({
    client: 'mysql2',
    connection: {
        host: 'nombreDelHost',
        user: 'usuario',
        password: 'c14v3$3cR3t4',
        database: 'nombreDeLaBaseDeDatos'
    },
    pool: {
        min: 0,
        max: 10
    }
});

El único cambio que introduciremos es el siguiente:

const db = require('mysql')({
    client: 'mysql2',
    connection: {
        host: 'nombreDelHost',
        user: 'usuario',
        password: 'c14v3$3cR3t4',
        database: 'nombreDeLaBaseDeDatos',
        typeCast: function castField(field, useDefaultTypeCasting) {
            if (field.type === "BIT" && field.length === 1) {
                let bytes = field.buffer();
                return bytes[0] === 1;
            }
            return useDefaultTypeCasting();
        }
    },
    pool: {
        min: 0,
        max: 10
    }
});

Si consumimos nuestro servicio posterior a realizar dicho cambio, notaremos que el campo que anteriormente regresaba como un búfer ahora se muestra como un verdadero dato de tipo booleano:

Esto nos evitará el tener que realizar conversiones y transformaciones cada vez que necesitemos evaluar un campo de dicho BIT siempre manteniendo la información en su estado original en la base de datos. Es también importante notar que esta evaluación se limita únicamente a campos que cumplan con las dos condiciones según la función que definimos: que sean de tipo BIT y cuya longitud sea de 1 byte. En este caso estamos asumiendo que si el campo cumple estas dos condiciones se trata de un campo booleano, sin embargo cualquier otra longitud u otro tipo de dato será ignorado y no se hará ninguna transformación.