DEV Community

Michael Mather
Michael Mather

Posted on • Updated on

DO Hackathon - Postgres and Sequelize models

As mentioned in a previous post, our app will be using a PostgreSQL database. To consolidate our database tables with data models in our code, we'll be using Sequelize.

I followed this brilliant post which outlines everything you'll need to know about connecting to the database and running migrations. I won't reiterate anything that's already in the post so instead I'll get right into our models and their relationships.

As you might remember, the purpose of our app is to help users keep track of when they complete habits and allow them to track their goal progression. We also want users to have the ability to login so that we can protect their data better. This means we'll likely be looking at models that look like this:

User:
  id: primary key
  name: string
  email: string
  password: string
  createdAt: timestamp

Habit:
  id: primary key
  userId: foreign key - users
  name: string
  description: string
  color: string
  weeklyGoal: number
  createdAt: timestamp

CompletedTask:
  id: primary key
  habitId: foreign key - inhalers
  dateCompleted: datetime
  createdAt: timestamp

CompletedGoal:
  id: primary key
  habitId: foreign key - inhalers
  weekStartDate: datetime
  createdAt: timestamp
Enter fullscreen mode Exit fullscreen mode

I found the Sequelize documentation a little confusing, because there's multiple ways to run migrations. Coming from a Django/Flask background I was used to the process of modifying your models, which would automatically create migrations. Sequelize allows us to do something similar using the sync() method. Here's how the models turned out:

Our User model:

// models/users.js
const { DataTypes } = require('sequelize')

module.exports = {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    name: {
      type: DataTypes.STRING,
      allowNull: false
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    },
    createdAt: {
      allowNull: false,
      type: DataTypes.DATE
    },
    updatedAt: {
      allowNull: false,
      type: DataTypes.DATE
    }
  }
Enter fullscreen mode Exit fullscreen mode

Our Habit model:

// models/habits.js
const { DataTypes } = require('sequelize')
module.exports = {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    name: {
      type: DataTypes.STRING,
      allowNull: false
    },
    description: {
      type: DataTypes.STRING,
      allowNull: false
    },
    color: {
      type: DataTypes.STRING,
      allowNull: false
    },
    weeklyGoal: {
      type: DataTypes.INTEGER,
      allowNull: false
    },
    createdAt: {
      allowNull: false,
      type: DataTypes.DATE
    },
    updatedAt: {
      allowNull: false,
      type: DataTypes.DATE
    }
}
Enter fullscreen mode Exit fullscreen mode

Our CompletedTask model:

// models/completedTask
const { DataTypes } = require('sequelize')

module.exports = {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    dateCompleted: {
      allowNull: false,
      type: DataTypes.DATE
    },
    createdAt: {
      allowNull: false,
      type: DataTypes.DATE
    },
    updatedAt: {
      allowNull: false,
      type: DataTypes.DATE
    }
}
Enter fullscreen mode Exit fullscreen mode

And lastly:

// models/completedGoals
const { DataTypes } = require('sequelize')

module.exports = {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    weekStartDate: {
      allowNull: false,
      type: DataTypes.DATE
    },
    createdAt: {
      allowNull: false,
      type: DataTypes.DATE
    },
    updatedAt: {
      allowNull: false,
      type: DataTypes.DATE
    }
}
Enter fullscreen mode Exit fullscreen mode



You'll notice that there's no foreign keys in these models. That's because sequelize allows us to define relationships using the .hasMany(), .haseOne() and .belongsTo() model functions.

Now that we've got our definitions, we need to initialize our models and sync up any changes with the database. I created an index.js file in the /models directory to do this:

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const envConfigs =  require('../config/config');
const userDef = require('./user');
const habitDef = require('./habit');
const completedTaskDef = require('./completedTask');
const completedGoalDef = require('./completedGoal');

const env = process.env.NODE_ENV || 'development';
const config = envConfigs[env];
const db = {};

// Establish the connection
const sequelize = new Sequelize(config.url, config);
db.sequelize = sequelize;
db.Sequelize = Sequelize;

// Define the models
const User = sequelize.define('User', userDef);
const Habit = sequelize.define('Habit', habitDef);
const CompletedTask = sequelize.define('CompletedTask', completedTaskDef);
const CompletedGoal = sequelize.define('CompletedGoal', completedGoalDef);

// Define the relationships between models
Habit.hasMany(CompletedTask, {
  onDelete: 'CASCADE'
});
Habit.hasMany(CompletedGoal, {
  onDelete: 'CASCADE'
});
CompletedTask.belongsTo(Habit);
CompletedGoal.belongsTo(Habit);
User.hasMany(Habit, {
  onDelete: 'CASCADE'
});
Habit.belongsTo(User);

// Sync any changes to the database
sequelize.sync({ alter: true })
db.Habit = Habit;
db.CompletedTask = CompletedTask;
db.CompletedGoal = CompletedGoal;
db.User = User;

module.exports = db;
Enter fullscreen mode Exit fullscreen mode

When we import this file into our main index.js, it'll connect to the database, initialize our models and sync any changes in our model definitions to our database tables. We can then import the db object and run a query like so:

const user = await db.User.findOne({
  where: { id: 1 }
 });
Enter fullscreen mode Exit fullscreen mode

We'll be using queries like that in the next post to setup password authentication for our users.

Top comments (0)