├── .gitignore
├── client
├── js
│ └── main.js
└── css
│ └── main.css
├── server
├── views
│ ├── index.html
│ ├── error.html
│ └── layout.html
├── config.json
├── models
│ └── index.js
├── routes
│ └── index.js
├── app.js
└── bin
│ └── www
├── .sequelizerc
├── package.json
├── README.md
└── gulpfile.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | npm-debug.log
4 | blog.md
5 |
--------------------------------------------------------------------------------
/client/js/main.js:
--------------------------------------------------------------------------------
1 | // add scripts
2 |
3 | $(document).on('ready', function() {
4 | console.log('sanity check!');
5 | });
6 |
--------------------------------------------------------------------------------
/client/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
9 |
--------------------------------------------------------------------------------
/server/views/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'layout.html' %}
2 |
3 | {% block title %}{% endblock %}
4 |
5 |
6 | {% block content %}
7 |
8 |
9 |
10 |
{{ title }}
11 |
Welcome to {{ title }}
12 |
13 |
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/.sequelizerc:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 |
3 | module.exports = {
4 | 'config': path.resolve('./server', 'config.json'),
5 | 'migrations-path': path.resolve('./server', 'migrations'),
6 | 'models-path': path.resolve('./server', 'models'),
7 | 'seeders-path': path.resolve('./server', 'seeders')
8 | }
9 |
--------------------------------------------------------------------------------
/server/views/error.html:
--------------------------------------------------------------------------------
1 | {% extends 'layout.html' %}
2 |
3 | {% block title %}{% endblock %}
4 |
5 |
6 | {% block content %}
7 |
8 |
9 |
10 |
{{ message }}
11 |
{{ error.status }}
12 |
{{ error.stack }}
13 |
14 |
15 |
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/server/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "username": "update me",
4 | "password": "update me",
5 | "database": "todos",
6 | "host": "127.0.0.1",
7 | "port": "5432",
8 | "dialect": "postgres"
9 | },
10 | "test": {
11 | "username": "update me",
12 | "password": "update me",
13 | "database": "update_me",
14 | "host": "update me",
15 | "dialect": "update me"
16 | },
17 | "production": {
18 | "username": "update me",
19 | "password": "update me",
20 | "database": "update me",
21 | "host": "update me",
22 | "dialect": "update me"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/server/views/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ title }}
6 |
7 |
8 |
9 |
10 | {% block content %}
11 | {% endblock %}
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "_example",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./server/bin/www"
7 | },
8 | "dependencies": {
9 | "body-parser": "~1.13.2",
10 | "cookie-parser": "~1.3.5",
11 | "debug": "~2.2.0",
12 | "express": "~4.13.1",
13 | "morgan": "~1.6.1",
14 | "pg": "^4.4.3",
15 | "pg-hstore": "^2.3.2",
16 | "sequelize": "^3.12.2",
17 | "sequelize-cli": "^2.1.0",
18 | "serve-favicon": "~2.3.0",
19 | "swig": "^1.4.2"
20 | },
21 | "devDependencies": {
22 | "browser-sync": "2.9.6",
23 | "gulp": "^3.9.0",
24 | "gulp-jshint": "^1.11.2",
25 | "gulp-nodemon": "^2.0.4",
26 | "jshint-stylish": "^2.0.1"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Node, Postgres, and Sequelize
2 |
3 | ## Want to learn how to build this project?
4 |
5 | Check out the [blog post](http://mherman.org/blog/2015/10/22/node-postgres-sequelize).
6 |
7 | ## Want to use this project?
8 |
9 | 1. Fork/Clone
10 | 1. Install dependencies - `npm install`
11 | 1. Create a local Postgres databases - `todos` - and then update *server/config.json*
12 | 1. Add a "migrations" folder to the "server" folder
13 | 1. Create two new migrations:
14 |
15 | ```sh
16 | $ node_modules/.bin/sequelize model:create --name Todo --attributes "title:string, complete:boolean,UserId:integer"
17 | $ node_modules/.bin/sequelize model:create --name User --attributes "email:string"
18 | ```
19 |
20 | 1. Update the migration files in "server/models" (if necessary) and then sync the database:
21 |
22 | ```sh
23 | $ node_modules/.bin/sequelize db:migrate
24 | ```
25 |
26 | 1. Run the development server - `gulp`
27 |
--------------------------------------------------------------------------------
/server/models/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var path = require('path');
5 | var Sequelize = require('sequelize');
6 | var basename = path.basename(module.filename);
7 | var env = process.env.NODE_ENV || 'development';
8 | var config = require(__dirname + '/../config.json')[env];
9 | var db = {};
10 |
11 | if (config.use_env_variable) {
12 | var sequelize = new Sequelize(process.env[config.use_env_variable]);
13 | } else {
14 | var sequelize = new Sequelize(config.database, config.username, config.password, config);
15 | }
16 |
17 | fs
18 | .readdirSync(__dirname)
19 | .filter(function(file) {
20 | return (file.indexOf('.') !== 0) && (file !== basename);
21 | })
22 | .forEach(function(file) {
23 | if (file.slice(-3) !== '.js') return;
24 | var model = sequelize['import'](path.join(__dirname, file));
25 | db[model.name] = model;
26 | });
27 |
28 | Object.keys(db).forEach(function(modelName) {
29 | if (db[modelName].associate) {
30 | db[modelName].associate(db);
31 | }
32 | });
33 |
34 | db.sequelize = sequelize;
35 | db.Sequelize = Sequelize;
36 |
37 | module.exports = db;
38 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Module Dependencies
3 | */
4 |
5 | var gulp = require('gulp');
6 | var jshint = require('gulp-jshint');
7 | var browserSync = require('browser-sync');
8 | var reload = browserSync.reload;
9 | var nodemon = require('gulp-nodemon');
10 |
11 |
12 | /**
13 | * Config
14 | */
15 |
16 | var paths = {
17 | styles: [
18 | './client/css/*.css',
19 | ],
20 | scripts: [
21 | './client/js/*.js',
22 | ],
23 | server: './server/bin/www'
24 | };
25 |
26 | var nodemonConfig = {
27 | script: paths.server,
28 | ext: 'html js css',
29 | ignore: ['node_modules']
30 | };
31 |
32 |
33 | /**
34 | * Gulp Tasks
35 | */
36 |
37 | gulp.task('lint', function() {
38 | return gulp.src(paths.scripts)
39 | .pipe(jshint())
40 | .pipe(jshint.reporter('jshint-stylish'));
41 | });
42 |
43 | gulp.task('browser-sync', ['nodemon'], function(done) {
44 | browserSync({
45 | proxy: "localhost:3000", // local node app address
46 | port: 5000, // use *different* port than above
47 | notify: true
48 | }, done);
49 | });
50 |
51 | gulp.task('nodemon', function (cb) {
52 | var called = false;
53 | return nodemon(nodemonConfig)
54 | .on('start', function () {
55 | if (!called) {
56 | called = true;
57 | cb();
58 | }
59 | })
60 | .on('restart', function () {
61 | setTimeout(function () {
62 | reload({ stream: false });
63 | }, 1000);
64 | });
65 | });
66 |
67 | gulp.task('watch', function() {
68 | gulp.watch(paths.scripts, ['lint']);
69 | });
70 |
71 | gulp.task('default', ['browser-sync', 'watch'], function(){});
72 |
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 | var models = require('../models/index');
4 |
5 |
6 | router.get('/', function(req, res, next) {
7 | res.render('index', { title: 'Express' });
8 | });
9 |
10 | router.post('/users', function(req, res) {
11 | models.User.create({
12 | email: req.body.email
13 | }).then(function(user) {
14 | res.json(user);
15 | });
16 | });
17 |
18 | // get all todos
19 | router.get('/todos', function(req, res) {
20 | models.Todo.findAll({}).then(function(todos) {
21 | res.json(todos);
22 | });
23 | });
24 |
25 | // get single todo
26 | router.get('/todo/:id', function(req, res) {
27 | models.Todo.find({
28 | where: {
29 | id: req.params.id
30 | }
31 | }).then(function(todo) {
32 | res.json(todo);
33 | });
34 | });
35 |
36 | // add new todo
37 | router.post('/todos', function(req, res) {
38 | models.Todo.create({
39 | title: req.body.title,
40 | UserId: req.body.user_id
41 | }).then(function(todo) {
42 | res.json(todo);
43 | });
44 | });
45 |
46 | // update single todo
47 | router.put('/todo/:id', function(req, res) {
48 | models.Todo.find({
49 | where: {
50 | id: req.params.id
51 | }
52 | }).then(function(todo) {
53 | if(todo){
54 | todo.updateAttributes({
55 | title: req.body.title,
56 | complete: req.body.complete
57 | }).then(function(todo) {
58 | res.send(todo);
59 | });
60 | }
61 | });
62 | });
63 |
64 | // delete a single todo
65 | router.delete('/todo/:id', function(req, res) {
66 | models.Todo.destroy({
67 | where: {
68 | id: req.params.id
69 | }
70 | }).then(function(todo) {
71 | res.json(todo);
72 | });
73 | });
74 |
75 | module.exports = router;
76 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | // *** main dependencies *** //
2 | var express = require('express');
3 | var path = require('path');
4 | var favicon = require('serve-favicon');
5 | var logger = require('morgan');
6 | var cookieParser = require('cookie-parser');
7 | var bodyParser = require('body-parser');
8 | var swig = require('swig');
9 |
10 |
11 | // *** routes *** //
12 | var routes = require('./routes/index.js');
13 |
14 |
15 | // *** express instance *** //
16 | var app = express();
17 |
18 |
19 | // *** view engine *** //
20 | var swig = new swig.Swig();
21 | app.engine('html', swig.renderFile);
22 | app.set('view engine', 'html');
23 |
24 |
25 | // *** static directory *** //
26 | app.set('views', path.join(__dirname, 'views'));
27 |
28 |
29 | // *** config middleware *** //
30 | app.use(logger('dev'));
31 | app.use(bodyParser.json());
32 | app.use(bodyParser.urlencoded({ extended: false }));
33 | app.use(cookieParser());
34 | app.use(express.static(path.join(__dirname, '../client')));
35 |
36 |
37 | // *** main routes *** //
38 | app.use('/', routes);
39 |
40 |
41 | // catch 404 and forward to error handler
42 | app.use(function(req, res, next) {
43 | var err = new Error('Not Found');
44 | err.status = 404;
45 | next(err);
46 | });
47 |
48 |
49 | // *** error handlers *** //
50 |
51 | // development error handler
52 | // will print stacktrace
53 | if (app.get('env') === 'development') {
54 | app.use(function(err, req, res, next) {
55 | res.status(err.status || 500);
56 | res.render('error', {
57 | message: err.message,
58 | error: err
59 | });
60 | });
61 | }
62 |
63 | // production error handler
64 | // no stacktraces leaked to user
65 | app.use(function(err, req, res, next) {
66 | res.status(err.status || 500);
67 | res.render('error', {
68 | message: err.message,
69 | error: {}
70 | });
71 | });
72 |
73 |
74 | module.exports = app;
75 |
--------------------------------------------------------------------------------
/server/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../app');
8 | var debug = require('debug')('_example:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort(process.env.PORT || '3000');
16 | app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app);
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | server.listen(port);
29 | server.on('error', onError);
30 | server.on('listening', onListening);
31 |
32 | /**
33 | * Normalize a port into a number, string, or false.
34 | */
35 |
36 | function normalizePort(val) {
37 | var port = parseInt(val, 10);
38 |
39 | if (isNaN(port)) {
40 | // named pipe
41 | return val;
42 | }
43 |
44 | if (port >= 0) {
45 | // port number
46 | return port;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | /**
53 | * Event listener for HTTP server "error" event.
54 | */
55 |
56 | function onError(error) {
57 | if (error.syscall !== 'listen') {
58 | throw error;
59 | }
60 |
61 | var bind = typeof port === 'string'
62 | ? 'Pipe ' + port
63 | : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 | /**
81 | * Event listener for HTTP server "listening" event.
82 | */
83 |
84 | function onListening() {
85 | var addr = server.address();
86 | var bind = typeof addr === 'string'
87 | ? 'pipe ' + addr
88 | : 'port ' + addr.port;
89 | debug('Listening on ' + bind);
90 | console.log('Listening on ' + bind);
91 | }
92 |
--------------------------------------------------------------------------------