0
votes

I am learning how to use Sequelize ORM in Nodejs and save data in Postgres Database.

My goal is to insert user data into Users table. I have created the table using migration, and it works. However, I am not able to save users data. I 've followed many resources for example Tut 1 Tut 2, etc.. , I still get the same error

C:\Users\HP\Desktop\path\project\Tutorials\react-project\chat_app_api\database\models\index.js:12
if (config.use_env_variable) {
           ^
TypeError: Cannot read property 'use_env_variable' of undefined
    at Object.<anonymous> (C:\Users\HP\Desktop\path\project\Tutorials\react-project\chat_app_api\database\models\index.js:12:12)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at babelWatchLoader (C:\Users\HP\Desktop\path\project\Tutorials\react-project\chat_app_api\node_modules\babel-watch\runner.js:51:13)    
    at Object.require.extensions.(anonymous function) [as .js] (C:\Users\HP\Desktop\path\project\Tutorials\react-project\chat_app_api\node_modules\babel-watch\runner.js:62:7)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Module.require (internal/modules/cjs/loader.js:690:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.<anonymous> (C:\Users\HP\Desktop\Andela\project\Tutorials\react-project\chat_app_api\server\server.js:1:1)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at babelWatchLoader (C:\Users\HP\Desktop\path\project\Tutorials\react-project\chat_app_api\node_modules\babel-watch\runner.js:51:13)    
    at Object.require.extensions.(anonymous function) [as .js] (C:\Users\HP\Desktop\path\project\Tutorials\react-project\chat_app_api\node_modules\babel-watch\runner.js:62:7)

config/config.js

require('dotenv').config();

module.exports = {
  development: {
    use_env_variable: 'DATABASE_URL_DEV',
    dialect: 'postgres',
  },
  test: {
    use_env_variable: 'DATABASE_URL_TEST',
    dialect: 'postgres',
  },
  production: {
    use_env_variable: 'DATABASE_URL',
    dialect: 'postgres',
    ssl: true,
    dialectOptions: {
      ssl: true,
    },
  },
};

migrations/20190927083519-create-user.js

'use strict';
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        primaryKey: true,
        type: Sequelize.UUID,
        defaultValue: Sequelize.UUIDV4,
      },
      fullname: {
        type: Sequelize.STRING
      },
      email: {
        type: Sequelize.STRING
      },
      password: {
        type: Sequelize.STRING
      },
      username: {
        type: Sequelize.STRING
      },
      telephone: {
        type: Sequelize.STRING
      },
      image: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('Users');
  }
};

models/index.js

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.js')[env];        // why this return Undefined ?
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = sequelize['import'](path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

models/users

'use strict';
module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    id: {
      type: DataTypes.UUID,
      defaultValue: DataTypes.UUIDV4,
      primaryKey: true,
    },
    fullname: DataTypes.STRING,
    email: DataTypes.STRING,
    password: DataTypes.STRING,
    username: DataTypes.STRING,
    telephone: DataTypes.STRING,
    image: DataTypes.STRING
  }, {});
  User.associate = function (models) {
    // associations can be defined here
  };
  return User;
};

app.js

import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import bodyParser from 'body-parser';
import { errors } from 'celebrate';

import routes from './Routes/index';

const app = express();

app.use(cors());
app.use(morgan('combined'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use('/api', routes);

app.use(errors());


app.use((req, res) => {
  const error = new Error('Route not found');
  error.status = 404;
  return res.status(error.status).json({
    status: error.status,
    message: error.message,
  });
});

// Server Error
app.use((error, req, res) => {
  const status = error.status || 500;
  return res.status(status).json({
    status,
    message: error.message || 'Server error',
  });
});

export default app;

.env

DATABASE_URL_DEV=postgres://postgres:.@localhost:5432/db_dev
DATABASE_URL_TEST=postgres://postgres:.@localhost:5432/db_test
DATABASE_URL=postgres://user:password@host:5432/db_remote

controllers/userControllers.js

import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

import models from '../../database/models';
import uploadImage from '../Helpers/upload.Image';

dotenv.config();

class UserController {
  static async signup(req, res) {
    const { body: input } = req;
    input.password = bcrypt.hashSync(input.password, 10);
    try {
      const image = await uploadImage(req, res);
      const { secure_url: img } = await image;

      input.image = img;
      console.log('result before ########################', models.User);         // Undefined
      const result = await models.User.create(input);
      console.log('result after ########################', result);                // Error here
      delete result.dataValues.password;

      const token = jwt.sign(result.dataValues, process.env.SECRET_KEY, { expiresIn: '1W' });
      result.dataValues.token = token;
      const status = 201;
      return res.status(status).json({
        status,
        message: 'User successfully created',
        data: result.dataValues,
      });
    } catch (error) {
      console.log('error########################', error);
      let { message } = error.errors[0];
      const status = 500;
      message = message || 'Server error';
      return res.status(status).json({
        status,
        message,
      });
    }
  }
}

export default UserController;

I still don't know why in models/index.js my config variable return undefined.

require(__dirname + '/../config/config.js')           // return object

env                                                   // return environment

const config = require(__dirname + '/../config/config.js')[env];                //return Undefined

I spent 3 days debugging but I can not solve the Error. any help, guidance is highly appreciated.

Thanks

1
What are you getting returned if you do: const config = require(__dirname + '/../config/config.js'); console.log(config[env]); ? - Dez
console.log(config[env]) returns the same error just UNDEFINED - Niyongabo
const config = require(__dirname + '/../config/config.js'); returns ``` { development: { use_env_variable: 'DATABASE_URL_DEV', dialect: 'postgres' }, test: { use_env_variable: 'DATABASE_URL_TEST', dialect: 'postgres' }, production: { use_env_variable: 'DATABASE_URL', dialect: 'postgres', ssl: true, dialectOptions: { ssl: true } } } ``` console.log(config[env]) returns the same error ``` C:\Users\HP\Desktop\path\project\Tutorials\react-project\chat_app_api\database\models\index.js:18 if (config.use_env_variable) { ... } ``` - Niyongabo
I can't see exactly the problem but my suspicion would be to move the line require('dotenv').config(); to the app.js file before the line where you import the routes. It is way way recommend to import the dotenv config as early as possible in your project. Also remove the line dotenv.config(); from the UserController. You just need it to import it once, not everytime that you need it. - Dez
@Dez Thanks for dotenv feedback. - Niyongabo

1 Answers

0
votes

Guyz, I found an answer to my problem,

in models/index.js

I change process.env.NODE_ENV to process.env.NODE_ENV.trim()

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);

// Before
const env = process.env.NODE_ENV || 'development';

// After
const env = process.env.NODE_ENV.trim() || 'development';    // add .trim()

const config = require(__dirname + '/../config/config.js')[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

...

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

Further Details

package.json

"scripts": {
    "db:migrate:dev": "sequelize db:migrate --env development",
    "db:migrate:test": "sequelize db:migrate --env test",
    "db:migrate:production": "sequelize db:migrate --env production",
    "db:reset": "sequelize db:migrate:undo",
    "start": "SET NODE_ENV=production && babel-watch server/server.js",
    "dev": "SET NODE_ENV=development && babel-watch server/server.js",
    "test": "SET NODE_ENV=testing && babel-watch server/server.js"
  }

Example, Let's say if I start the server by typing in the terminal

npm run dev 
If i do console.log(process.env.NODE_ENV)  // output is "development " with a space.

Hence, 
 process.env.NODE_ENV === "development"  // return false
 or
 "development " === "development" // return false

Javascript Trim() remove whitespace from both sides of a string

You want more resource? please visit w3c