Cannot Read Property 'use_env_variable' of Undefined Sequelize
Using PostgreSQL and Sequelize to persist our information
It's been quite a journey since the part i of this serial, In this tutorial, we will persist our information with a database using Postgres and Sequelize. Sequelize is an ORM for Nodejs, it supports PostgreSQL, MySQL, SQLite and MSSQL. the source lawmaking for this tutorial can exist establish here
Configure Sequelize
To get started we'll get-go by installing some dependencies. First, nosotros'll installsequelize-cli
`, sequelize-cli creates a command line interface for us to run Sequelize related commands in our app.
$ npm install -salve sequelize-cli
To configure Sequelize for our projection we are going to create a .sequelizerc file in the root of our app
$ impact .sequelizerc
inside the file type the post-obit
const path = require('path');module.exports = {
"config": path.resolve('./config', 'config.json'),
"
models-path ": path.resolve('./models'),
" migrations-path ": path.resolve('./migrations')
};
The sequelizerc file is going to bootstrap our application with the above paths, the config.json
file is going to incorporate the configuration for our application, the models path volition contain our application models, migrations
will comprise the different migrations for our application. the migration files are used to create our app's tabular array using the models that we create. the models contain the design for our application tables
The next thing to do is to install Sequelize and another dependencies,
$ npm install --save sequelize pg pg-hstore
pg
is a PostgreSQL client for Node.js, pg is responsible for creating our application'south database connectedness. pg-hstore
is a node parcel for serializing and deserializing JSON data to hstore format. read more about pg-hstore here.
Adjacent run
$ sequelize init
this is going to bootstrap our application with the path specified in .sequelizerc
file. After running information technology you should have a config folder which contains a config.json
file, a model folder which contains some configurations for the models and a migration folder.
let's go through the index.js file in the models folder, some required node modules were imported, the line with config.use_env_variable
checks if whatsoever environment variable is prepare, if it is set then nosotros use the settings for that environment variable otherwise we use the alternative settings provided for that surroundings. the process.env gives admission to the node env, but this will non piece of work unless we have a dotenv package installed, this volition give u.s. access to the node surround from our application.
Configure dotenv
$ npm install --save dotenv
afterward installation, require the dotenv file as early as possible in your application. I'll require mine at the superlative of index.js
file in the models folder
require('dotenv').config()
we required the dotenv module and call the method config on it. the next thing we need to do is to create .env
file, this volition contain all our node environment variables. At the root of your app create a .env
file
$ touch .env
now to prepare Postgres for our application we can either use the settings provided in the config.json file, we'll only supplant the required information with the information we Setup when nosotros installed Postgres or we can use an surroundings variable. in the development section, delete everything there and supervene upon it with this
"development": { "use_env_variable": "DATABASE_URL"
},
now our model configuration is going to make use of this surroundings variable when we are on development. think nosotros had something like this in our model:
if (config.use_env_variable) {
var sequelize = new
Sequelize(process.env[config.use_env_variable], config); } else { var sequelize = new Sequelize(config.database, config.username,
config.countersign, config); }
so nosotros are checking if our config is set up, in this case, our config variable is set to development object, this line of lawmaking is doing that.
var env = process.env.NODE_ENV || 'development'; var config = require(__dirname + '/../config/config.json')[env];
we prepare the env to development by default, then we required the config.json file and pull out the development surroundings. so config
variable is equal to our development object set up in config.json
file. and then we then check if use_env_variable
is ready, if it is we use procedure.env[config.use_env_variable]
which translates to procedure.env['DATABASE_URL']
to get the value set in that environment. but we don't take DATABASE_URL
` fix in our node environment. to exercise that nosotros volition go to our .env
file and set it, so the .env file is where we gear up custom node_env we want to have access to in our code. in your .env file type the following:
DATABASE_URL=postgres://username:countersign@localhost:5432/database_name
If you don't have a countersign setup and so your configuration should be in this format:
DATABASE_URL=postgres://username@localhost:5432/database_name
and so our models will brand use of this DATABASE_URL
to set up our database, replace username with your Postgres username, mine is postgres
, replace password with your password and database_name with the name of your database. so in my case, we'll accept something like this:
DATABASE_URL=postgres://postgres:mypassword@localhost:5432/todo-app
then create your Postgres database from your concluding like this:
$ createdb todoapp
ensure you accept Postgres installed otherwise this will not piece of work.
note that anything we set up in our .env file is available in our node environment and can be accessed with process.env.whatWeSet
in our case procedure.env.DATABASE_URL
`. NODE_ENV
is equal to whatever we set it to be. we could fix it to development, production or testing, depending on what environment nosotros want to work on. we could fix information technology in the .env file also. In our case, if it is not set we just default to evolution: var env = process.env.NODE_ENV || 'development'
.
Creating our models
Now that we have our configurations out of the fashion, the next thing is to create our models, Each todo is going to have todo items, so a todo can have many items under it, so we can then say that the relationship between a todo and the todo item is a ane to many human relationship. Now to create this relationship we showtime have to create our models, run the following control to create our models
Todo models
$ node_modules/.bin/sequelize model:create --name Todo --attributes title:string
- — name refers to the name of the model
- — attribute refers to the attributes the model should have
this will generate a todo.js file in our model and the todo model should look something like this:
'utilize strict'; module.exports = (sequelize, DataTypes) => { var Todo = sequelize.define('Todo', { title: DataTypes.STRING }, {}); Todo.associate = function(models) { // associations can be defined here }; return Todo; };
A migration file volition also be automatically generated in the migration folder and information technology should wait similar this:
'use strict'; module.exports = { upward: (queryInterface, Sequelize) => { return queryInterface.createTable('Todos', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, title: { type: Sequelize.Cord }, createdAt: { allowNull: imitation, type: Sequelize.Engagement }, updatedAt: { allowNull: imitation, type: Sequelize.Engagement } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Todos'); } };
TodoItem
We'll do the aforementioned thing for todo items, run the post-obit command in your terminal
$ node_modules/.bin/sequelize model:create --proper name TodoItem --attributes description:string
the todoItem model should wait something like this:
'apply strict'; module.exports = (sequelize, DataTypes) => { var TodoItem = sequelize.ascertain('TodoItem', { clarification: DataTypes.STRING }, {}); TodoItem.associate = function(models) { // associations can be defined hither }; render TodoItem; };
the migration file
'use strict'; module.exports = { upward: (queryInterface, Sequelize) => { return queryInterface.createTable('TodoItems', { id: { allowNull: imitation, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, clarification: { type: Sequelize.Cord }, createdAt: { allowNull: faux, blazon: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('TodoItems'); } };
Refactoring
nosotros are going to refactor some part of our code to make use of Es6, change the functions to arrow functions and all vars to const
module.exports = (sequelize, DataTypes) => { const Todo = sequelize.define('Todo', { championship: { type: DataTypes.STRING, allowNull: false, }, }); Todo.associate = (models) => { // associations can be defined hither Todo.hasMany(models.TodoItem, { foreignKey: 'todoId', }); }; return Todo; };
in the higher up code we modified our todo.js file, first we included an extra aspect to the championship field allowNull: false
what this attribute does is that it made the title field not nullable, significant that the database is going to throw an error if we try to add an empty or null value to the championship field, the DataTypes.string means that we simply expect a string value in this field, anything other than that the database is going to throw an fault
Also, we created a human relationship between out Todo and TodoItem, similar nosotros said before, every single Todo has many todoItem, that relationship is divers with the hasMany many method of the Todo Model. The foreignKey: 'todoId', means that todoId is going to be the strange fundamental cavalcade in todoItem, you can read more than most foreign keys here.
we will as well make the same modification to ur TodoItem
module.exports = (sequelize, DataTypes) => { const TodoItem = sequelize.ascertain('TodoItem', { description: { blazon: DataTypes.String, allowNull: false, }, }); TodoItem.acquaintance = (models) => { // associations tin can be divers hither TodoItem.belongsTo(models.Todo, { foreignKey: 'todoId', onDelete: 'Cascade', }); }; return TodoItem; };
Every unmarried Todo has many TodoItems and each TodoItem belongs to one Todo, that is why we have the TodoItem.belongsTo define in our model above. The onDelete: 'Cascade' means if we delete a todo and so the associated todoITem should as well be deleted
Now that we accept our models ready we tin now run our migrations, in the migration folder we'll have a ready of migration files, The files contain scripts for creating and dropping our database tables, the migration scripts were created to model what we defined in our models, again, the scripts are responsible for creating and dropping our tables every time we run it. The up part is responsible for creating our tables and its columns, the down function is responsible for undoing what the up function runs.
we are going to exist modifying our migration scripts since we modified our model files, this is to ensure consistency between our model and migrations
since we are going to be having a foreingkey: todoId in the TodoItem as we defined in our models, we are going to change the migration script for TodoItem to include a todoId
we are going to include this in the migration file for TodoItem
todoId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Todos',
fundamental: 'id',
every bit: 'todoId',
},
},
the references contains a belongings called model, this tells us the model this ForeignKey refers to, in our case information technology is the Todos, the adjacent attribute is the key attribute which tells us what the todoId in todoItems maps to in Todos models, in this example it is the id of the Todos table, what this means is that the id of the todo table is the same as the todoId in the TodoItem table
the new migrations file should wait something like this:
module.exports = { upwardly: (queryInterface, Sequelize) => { render queryInterface.createTable('TodoItems', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, description: { type: Sequelize.String }, createdAt: { allowNull: false, blazon: Sequelize.Appointment }, updatedAt: { allowNull: faux, type: Sequelize.Engagement }, todoId: { type: Sequelize.INTEGER, onDelete: 'Cascade', references: { model: 'Todos', key: 'id', every bit: 'todoId', }, }, }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('TodoItems'); } };
Now we are fix to run our migration, running migrations we run the script in the migrations files and create our tables. Before running the migrations, on your last export your database url like this:
consign DATABASE_URL=postgres://postgres@localhost:5432/database_name
Run the migration like this:
node_modules/.bin/sequelize db:migrate
you should encounter something like this run in your terminal
== 20180908120703-create-todo: migrating =======
== 20180908120703-create-todo: migrated (0.025s) == 20180908121020-create-todo-detail: migrating =======
== 20180908121020-create-todo-item: migrated (0.013s)
If you check your database you volition see that your table has been created and the relationship has been defined
Now information technology is time to persist our information, to interact with the database we created nosotros are going to be using the models we created, with the models we have access to enormous methods we can use to interact with our database, we tin perform Grime operations through this methods,
In the controller of our todo app i.e todo.js
import the model
import models from '../models';
This will get to the index.js file
and import db
exported at the lesser of the file. through this model, we can interact with the database.
Create Todo
Now let'due south modify createTodo
method, the method to create todo currently looks like this
createTodo(req, res) { if (!req.torso.title) { return res.status(400).send({ success: 'simulated', message: 'title is required', }); } else if (!req.trunk.clarification) { return res.status(400).transport({ success: 'imitation', message: 'description is required', }); } const todo = { id: db.length + ane, title: req.body.title, clarification: req.torso.description, }; db.push button(todo); render res.status(201).send({ success: 'true', message: 'todo added successfully', todo, }); }
Now instead of using db.push button(todo)
to push a new todo to our db object
we are going to make use of the create method provided past our models, as well we'll remove the description field since the todos tabel does not take a field for description, the description will be added in the todoItem table
Now our createTodo method looks like this:
createTodo(req, res) { if (!req.body.title) { return res.condition(400).ship({ success: 'false', message: 'championship is required', }); } const todo = { title: req.body.championship, }; models.Todo.create(todo).then((todo) => { return res.condition(201).send({ success: 'true', bulletin: 'todo added successfully', todo, }); }); }
The .then
function runs when the todo has been successfully added to the database, the parameter passed into the function in the .so
is the todo response from the database after the create functioning is completed, so what we are basically proverb in a layman language is that the line of lawmaking should create a todo in the database, and because a database interaction will take a few milliseconds to complete nosotros don't desire to wait for that operation to end before nosotros move to the next line of code, so we go along to the next line of code but when that database performance finish executing the code in the .so
should run with the returned consequence which we passed as a parameter called todo
to the function. This whole functioning is called Promises in javascript, you can study more than on promises here.
Our returned response looks similar this:
Now we have a problem, we can create another todo with the same title and it will be added to the database, we need a fashion to ensure that a todo with same title doesn't get added to the database. To do this we have to starting time check if the todo we are trying to add together to the database exists already, if it does then we render a bulletin to the user, if it doesn't exist already then we add it to the database. so we have to search the database for the title that the user provide to united states of america, if the title exists already so we will render a message to the user, otherwise, nosotros'll add the todo top the database
Later on implementing this our createTodo should now look like this
createTodo (req, res) { if (!req.torso.championship) { return res.status(400).transport({ success: 'false', message: 'title is required', }); } models.Todo.findOne({ where: { championship: req.body.championship } }) .and so((todoFound) => { if (todoFound) { render res.status(403).send({ success: 'true', bulletin: 'A todo with that championship exist already', }); } const todo = { title: req.trunk.title, }; models.Todo.create(todo).then((todo) => { return res.status(201).send({ success: 'truthful', bulletin: 'todo added successfully', todo, }); }); }) }
Now we are using the findOne
method to check if a todo with the championship provided already exist, if information technology does the we return a message, otherwise we get ahead to add the todo.
Attempting to add a Todo that already exist our response looks similar this
Go All Todos
Now let's modify our getAllTodos
method, the lawmaking currently looks similar this
getAllTodos(req, res) { return res.status(200).send({ success: 'true', message: 'todos retrieved successfully', todos: db, }); }
Now we need to make utilise of the findAll
method provided past our models to observe all todos
the modification looks like this:
getAllTodos(req, res) { models.Todo.findAll() .then(todos => res.status(200).transport({ success: 'truthful', bulletin: 'todos retrieved successfully', todos, })); }
the response looks similar this:
Become a unmarried Todo
the old code for get a single todo
getTodo(req, res) { const id = parseInt(req.params.id, x); db.map((todo) => { if (todo.id === id) { return res.status(200).send({ success: 'true', message: 'todo retrieved successfully', todo, }); } }); return res.status(404).send({ success: 'false', message: 'todo does not exist', }); }
The new code looks like this:
getTodo(req, res) { const id = parseInt(req.params.id, ten); models.Todo.findById(id) .so((todo) => { if (todo) { render res.status(200).transport({ success: 'true', message: 'todo retrieved successfully', todo, }); } return res.status(404).transport({ success: 'false', message: 'todo does not exist', }); }); }
We utilise the findById
method and pass the Id of the todo we wish to get, if the todo exist then we get a response and if the todo does not exist we get a response telling us that the todo does not exist
Note: If you lot are using sequelize v5, findById was replaced by findByPk
Todo that does not exist in the database
Decision
This marks the finish of this tutorial, as practised and before the next part of this serial you can change the endpoint for update and delete to make employ of our models, Also we'll not be working with the todoItem model we created, y'all can take that up as a challenge and add description to the todos that nosotros already created. Cheers!!
refrence
Source: https://medium.com/@haybams/using-postgresql-and-sequelize-to-persist-our-data-c86854a3c6ac
0 Response to "Cannot Read Property 'use_env_variable' of Undefined Sequelize"
Publicar un comentario