I once had to work with a project which used MariaDB connector for Node.js, and for some reasons it was necessary to add Knex query builder to it. Honestly, I am not a big fan of query builders, because I know SQL pretty well, and usually it is faster for me to write a query in SQL than use a query builder. But sometimes you have to obey someone else’s decisions 🙂
The main issue was that Knex does not support MariaDB connector out of the box: it supports only a few drivers, and MariaDB is unfortunately not one of them (you can use either mysql or mysql2).
However, because the project used mariadb
, I did not want to switch to mysql
or mysql2
, and decided to write a MariaDB client for Knex.
It turned out that there had been no simple way to extend Knex: even the list of supported clients was immutable:
const SUPPORTED_CLIENTS = Object.freeze( [ 'mssql', 'mysql', 'mysql2', 'oracledb', 'postgres', 'redshift', 'sqlite3', ].concat(keys(CLIENT_ALIASES)) );
And if you try to pass anything else as client
, you get an error: “Error: knex: Unknown configuration option ‘client’ value XXX. Note that it is case-sensitive, check documentation for supported values.”
The official documentation was unhelpful there, so I had to dig into the code.
It turned out that:
- If no parameters are passed to Knex, it uses a generic client (whatever it means).
- If
config.client
is a function, and is derived fromClient
, the function will be used as a client. - Otherwise, client name is taken from
config.client
orconfig.dialect
and is expected to be a string. If the name is not inSUPPORTED_CLIENTS
, Knex throws an exception, otherwise it loads the client fromdialects/<client>/index.js
.
To create a client (or dialect), I had to use the second option. Fortunately, mariadb connector can be compatible with mysql and mysql2: it provides a callback API for compatibility reasons. This means that I don’t have to write everything from scratch, and it should be enough to rewrite the existing mysql2
dialect.
Basically, I had to re-implement only two things: _driver()
and validateConnection()
methods. The first one returns the driver (or connector) — mariadb
in my case, and the second one checks whether the connection is valid (mariadb
does that differently).
And here is the final code:
const Client_MySQL = require('knex/src/dialects/mysql/index'); class Client_MariaDB extends Client_MySQL { driverName = 'mariadb'; _driver () { return require('mariadb/callback'); } validateConnection (connection) { return connection.isValid(); } }; module.exports = Client_MariaDB;
Thanks for explaining the reasoning and logic of the connector you have created. It was helpful.
You may also add a link to project README.
thanks for this detailed solution. Appreciate your sharing of repo too. https://github.com/sjinks/knex-mariadb/tree/master