Guide to using Sequelize with Postgres

Below notes are w.r.t. Sequelize v6

Mapping of data types across Sequelize, Postgres and JavaScript

Below is not exhaustive list but covers some advanced types:

Sequelize Postgres JavaScript Read Write
DataTypes.INTEGER integer number number number
DataTypes.DECIMAL numeric decimal.js string Decimal
DataTypes.STRING character varying (255) string string string
DataTypes.UUID uuid uuid string string
DataTypes.DATEONLY date Date string Date
DataTypes.DATE TIMESTAMP WITH TIME ZONE Date Date Date

Read is what you get when you read using Sequelize. Write is what you can write to database using Sequelize. They are explained more in following sections.

declare properties to fix TypeScript errors

Consider a model defined like this:

class Foo extends Model { 
}

Foo.init({
    // WARNING: note this does not prevent an empty field (empty UUID) even though we set allowNull: false
    id: { type: DataTypes.UUID, allowNull: false, field: 'booking_id' }, 
}, {
    // Other model options go here
    sequelize: sql, // We need to pass the connection instance
    tableName: 'bookings'
});

If you are using TypeScript, the TypeScript compiler will complain when you try to access a property like so:

foo.id;

To fix this, we need to declare id like so:

class Foo extends Model { 
    declare id: string;
}

Reading and Writing UUIDs

Read UUID

A field defined as DataTypes.UUID will come out as a string when read from the database.

Write UUID

A field defined as DataTypes.UUID can be assigned a string (e.g., uuid.v4()) before writing to the database.

Reading and Writing Dates

Read Date

A field defined as DataTypes.DATEONLY will come out as a string when read from the database. To convert it into a JavaScript Date object, you can define a custom getter like so:

endDate: { type: DataTypes.DATEONLY, allowNull: false, get() { return parseISO(this.getDataValue('endDate')) }, field: 'end_date' },

Write Date

A field defined as DataTypes.DATEONLY can hold a JavaScript Date before writing to the database.

Reading and Writing Timestamps

Read Timestamp

A field defined as DataTypes.DATE will come out as a JavaScript Date object when read from the database. This is due to the way the underlying pg library works. As documented here:

node-postgres will convert instances of JavaScript date objects into the expected input value for your PostgreSQL server. Likewise, when reading a date, timestamp, or timestamptz column value back into JavaScript, node-postgres will parse the value into an instance of a JavaScript Date object.

Write Timestamp

As mentioned above, a field defined as DataTypes.DATE can hold a JavaScript Date before writing to the database.

Reading and Writing Decimals (or INT8)

Reading Decimal

A field defined as DataTypes.DECIMAL (or DataTypes.BIGINT) will come out as a JavaScript string when read from the database! This is because of the way the underlying pg library works. see:

By default the PostgreSQL backend server returns everything as strings.

You can convert it to a number by defining a custom getter (refer this):

dollarValue: { type: DataTypes.DECIMAL, allowNull: false, get() { return parseFloat(this.getDataValue('dollarValue'))}, field: 'dollar_value' }

or even convert it to a decimal (requires the decimal.js library):

dollarValue: { type: DataTypes.DECIMAL, allowNull: false, get() { return new Decimal(this.getDataValue('dollarValue'))}, field: 'dollar_value' }

WARNING: The established pattern of using types.setTypeParser to parse BIGINT and DECIMAL columns does NOT work with Sequelize. Refer this thread for details. This is quite unfortunate and one of the things I dislike about Sequelize (but not a deal-breaker).

Writing Decimal

A field defined as DataTypes.DECIMAL can hold a Decimal before writing to the database.

Fun Fact

decimal is not same as decimal.js!

> var Decimal = require('decimal.js')
undefined
> var D2 = require('decimal')
undefined
> x = new Decimal('-100.001')
-100.001
> y = new D2('-200.0002')
Decimal {
  internal: '-200.0002',
  as_int: { value: -2000002, exp: -4 },
  add: [Function (anonymous)],
  sub: [Function (anonymous)],
  mul: [Function (anonymous)],
  div: [Function (anonymous)],
  toString: [Function (anonymous)],
  toNumber: [Function (anonymous)]
}
> y.toFixed
undefined
> x.toFixed
[Function (anonymous)]

Note that you can JOIN tables with Sequelize without creating foreign keys. Refer this.

Further Reading

This entry was posted in Computers, programming, Software and tagged . Bookmark the permalink.

Leave a comment