Extending Data Types

Most likely the type you are trying to implement is already included in DataTypes. If a new datatype is not included, this manual will show how to write it yourself.

Sequelize doesn’t create new datatypes in the database. This tutorial explains how to make Sequelize recognize new datatypes and assumes that those new datatypes are already created in the database.

To extend Sequelize datatypes, do it before any Sequelize instance is created.

Example

In this example, we will create a type called SOMETYPE that replicates the built-in datatype DataTypes.INTEGER(11).ZEROFILL.UNSIGNED.

  1. const { Sequelize, DataTypes, Utils } = require('Sequelize');
  2. createTheNewDataType();
  3. const sequelize = new Sequelize('sqlite::memory:');
  4. function createTheNewDataType() {
  5. class SOMETYPE extends DataTypes.ABSTRACT {
  6. // Mandatory: complete definition of the new type in the database
  7. toSql() {
  8. return 'INTEGER(11) UNSIGNED ZEROFILL'
  9. }
  10. // Optional: validator function
  11. validate(value, options) {
  12. return (typeof value === 'number') && (!Number.isNaN(value));
  13. }
  14. // Optional: sanitizer
  15. _sanitize(value) {
  16. // Force all numbers to be positive
  17. return value < 0 ? 0 : Math.round(value);
  18. }
  19. // Optional: value stringifier before sending to database
  20. _stringify(value) {
  21. return value.toString();
  22. }
  23. // Optional: parser for values received from the database
  24. static parse(value) {
  25. return Number.parseInt(value);
  26. }
  27. }
  28. // Mandatory: set the type key
  29. SOMETYPE.prototype.key = SOMETYPE.key = 'SOMETYPE';
  30. // Mandatory: add the new type to DataTypes. Optionally wrap it on `Utils.classToInvokable` to
  31. // be able to use this datatype directly without having to call `new` on it.
  32. DataTypes.SOMETYPE = Utils.classToInvokable(SOMETYPE);
  33. // Optional: disable escaping after stringifier. Do this at your own risk, since this opens opportunity for SQL injections.
  34. // DataTypes.SOMETYPE.escape = false;
  35. }

After creating this new datatype, you need to map this datatype in each database dialect and make some adjustments.

PostgreSQL

Let’s say the name of the new datatype is pg_new_type in the postgres database. That name has to be mapped to DataTypes.SOMETYPE. Additionally, it is required to create a child postgres-specific datatype.

  1. function createTheNewDataType() {
  2. // [...]
  3. const PgTypes = DataTypes.postgres;
  4. // Mandatory: map postgres datatype name
  5. DataTypes.SOMETYPE.types.postgres = ['pg_new_type'];
  6. // Mandatory: create a postgres-specific child datatype with its own parse
  7. // method. The parser will be dynamically mapped to the OID of pg_new_type.
  8. PgTypes.SOMETYPE = function SOMETYPE() {
  9. if (!(this instanceof PgTypes.SOMETYPE)) {
  10. return new PgTypes.SOMETYPE();
  11. }
  12. DataTypes.SOMETYPE.apply(this, arguments);
  13. }
  14. const util = require('util'); // Built-in Node package
  15. util.inherits(PgTypes.SOMETYPE, DataTypes.SOMETYPE);
  16. // Mandatory: create, override or reassign a postgres-specific parser
  17. // PgTypes.SOMETYPE.parse = value => value;
  18. PgTypes.SOMETYPE.parse = DataTypes.SOMETYPE.parse || x => x;
  19. // Optional: add or override methods of the postgres-specific datatype
  20. // like toSql, escape, validate, _stringify, _sanitize...
  21. }

Ranges

After a new range type has been defined in postgres, it is trivial to add it to Sequelize.

In this example the name of the postgres range type is SOMETYPE_range and the name of the underlying postgres datatype is pg_new_type. The key of subtypes and castTypes is the key of the Sequelize datatype DataTypes.SOMETYPE.key, in lower case.

  1. function createTheNewDataType() {
  2. // [...]
  3. // Add postgresql range, SOMETYPE comes from DataType.SOMETYPE.key in lower case
  4. DataTypes.RANGE.types.postgres.subtypes.SOMETYPE = 'SOMETYPE_range';
  5. DataTypes.RANGE.types.postgres.castTypes.SOMETYPE = 'pg_new_type';
  6. }

The new range can be used in model definitions as DataTypes.RANGE(DataTypes.SOMETYPE) or DataTypes.RANGE(DataTypes.SOMETYPE).