├── .env
├── .gitignore
├── app.js
├── config.js
├── controllers
└── home.js
├── docker-compose.yml
├── frontend
├── index.js
├── js
│ └── .gitkeep
├── jsx
│ └── .gitkeep
└── less
│ └── .gitkeep
├── models
├── init.js
└── posts.js
├── package.json
├── public
├── .gitkeep
└── images
│ └── chase.png
├── readme.md
├── var
├── docker
│ ├── mysql
│ │ └── .gitkeep
│ └── redis
│ │ └── .gitkeep
└── sample_schema.sql
├── views
├── home
│ └── home.twig
└── master.twig
└── webpack.config.js
/.env:
--------------------------------------------------------------------------------
1 | COMPOSE_PROJECT_NAME=expressmvc
2 | APPENV=development
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | node_modules
3 | var/docker/mysql/*
4 | !var/docker/mysql/.gitkeep
5 | var/docker/redis/*
6 | !var/docker/redis/.gitkeep
7 | public/main.bundle.js
8 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | require('nocamel');
2 |
3 | // global config
4 | config = require('./config');
5 |
6 | // set up express
7 | const express = require('express');
8 | const redis = require('redis');
9 | const session = require('express-session');
10 | const store = require('connect-redis')(session);
11 | const app = express();
12 |
13 | let redis_client = redis.createClient({ host: 'redis', port: 6379 });
14 |
15 | app.set('view engine', 'twig');
16 | app.set('views', './views');
17 | app.use(express.urlencoded({ extended: true }));
18 | app.use(express.static('./public'));
19 | app.use(session({
20 | store: new store({ client: redis_client }),
21 | secret: 'whatever',
22 | resave: false,
23 | saveUninitialized: true
24 | }));
25 |
26 | // global database object
27 | db = require('./models/init');
28 |
29 | // each controller file
30 | const home = require('./controllers/home');
31 |
32 | // your routes
33 | app.get('/', home.home); // routes url / to controllers/home.js home method
34 | app.get('/home', home.home); // routes url /home to controllers/home.js home method
35 |
36 | // start the app
37 | app.listen(8000, () => {});
38 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | environment: process.env.APPENV || 'development',
4 | database: {
5 | username: 'root',
6 | password: 'root',
7 | database: 'testdb',
8 | host: 'mysql',
9 | dialect: 'mysql',
10 | logging: false,
11 | timezone: '+00:00',
12 | define: {
13 | underscored: true,
14 | timestamps: false
15 | }
16 | }
17 |
18 | };
19 |
--------------------------------------------------------------------------------
/controllers/home.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | async home(req, res) {
4 | let posts = await db.posts
5 | .find_all();
6 |
7 | // this says: render the views/home/home.twig file and give it the posts variable to use
8 | return res.render('home/home', {
9 | posts
10 | });
11 | }
12 |
13 | };
14 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | app:
4 | container_name: expressmvc_app
5 | image: node:14.4.0
6 | ports:
7 | - "8000:8000"
8 | volumes:
9 | - ./:/opt/src
10 | command: bash -c 'cd /opt/src && npm install && npm start'
11 | environment:
12 | APPENV: ${APPENV}
13 | mysql:
14 | container_name: expressmvc_mysql
15 | image: mysql:8.0
16 | environment:
17 | MYSQL_ROOT_PASSWORD: root
18 | MYSQL_DATABASE: testdb
19 | command: --init-file /var/lib/init/sample_schema.sql
20 | volumes:
21 | - ./:/opt/src
22 | - ./var/docker/mysql:/var/lib/mysql
23 | - ./var/sample_schema.sql:/var/lib/init/sample_schema.sql
24 | redis:
25 | container_name: expressmvc_redis
26 | image: redis:5.0.3
27 | volumes:
28 | - ./var/docker/redis:/data
29 |
--------------------------------------------------------------------------------
/frontend/index.js:
--------------------------------------------------------------------------------
1 | import 'core-js/stable';
2 | import 'regenerator-runtime/runtime';
3 |
4 | (ctx => {
5 | return ctx.keys().map(ctx);
6 | })(require.context('./js', true, /\.js$/));
7 |
8 | (ctx => {
9 | return ctx.keys().map(ctx);
10 | })(require.context('./jsx', true, /\.jsx$/));
11 |
12 | (ctx => {
13 | return ctx.keys().map(ctx);
14 | })(require.context('./less', true, /\.less$/));
15 |
--------------------------------------------------------------------------------
/frontend/js/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engineer-man/express-mvc-skeleton/cd4cfe99eebbaee89a70071dcef99808138945b5/frontend/js/.gitkeep
--------------------------------------------------------------------------------
/frontend/jsx/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engineer-man/express-mvc-skeleton/cd4cfe99eebbaee89a70071dcef99808138945b5/frontend/jsx/.gitkeep
--------------------------------------------------------------------------------
/frontend/less/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engineer-man/express-mvc-skeleton/cd4cfe99eebbaee89a70071dcef99808138945b5/frontend/less/.gitkeep
--------------------------------------------------------------------------------
/models/init.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const basename = path.basename(module.filename);
3 | const db = {};
4 |
5 | const Sequelize = require('sequelize');
6 | const sequelize = new Sequelize(
7 | config.database.database,
8 | config.database.username,
9 | config.database.password,
10 | config.database
11 | );
12 |
13 | require('fs')
14 | .readdir_sync(__dirname)
15 | .filter(file => {
16 | return file.indexOf('.') !== 0 && file !== basename && file.match(/\.js$/);
17 | })
18 | .for_each(file => {
19 | let model = sequelize.import(path.join(__dirname, file));
20 | db[model.name] = model;
21 | });
22 |
23 | for (const model_name in db) {
24 | db[model_name].bulk_create = db[model_name].bulkCreate;
25 | db[model_name].find_one = db[model_name].findOne;
26 | db[model_name].find_all = db[model_name].findAll;
27 | db[model_name].find_or_create = db[model_name].findOrCreate;
28 | db[model_name].find_and_count_all = db[model_name].findAndCountAll;
29 | db[model_name].belongs_to = db[model_name].belongsTo;
30 | db[model_name].has_one = db[model_name].hasOne;
31 | db[model_name].has_many = db[model_name].hasMany;
32 | db[model_name].belongs_to_many = db[model_name].belongsToMany;
33 | }
34 |
35 | db.sequelize = sequelize;
36 | db.Sequelize = Sequelize;
37 |
38 | $or = Sequelize.Op.or;
39 | $and = Sequelize.Op.and;
40 | $ne = Sequelize.Op.ne;
41 | $not = Sequelize.Op.not;
42 |
43 | module.exports = db;
44 |
--------------------------------------------------------------------------------
/models/posts.js:
--------------------------------------------------------------------------------
1 | const Sequelize = require('sequelize');
2 | const moment = require('moment');
3 |
4 | module.exports = (sequelize, DataTypes) => {
5 | class posts extends Sequelize.Model { }
6 |
7 | posts.init(
8 | {
9 | post_id: {
10 | type: DataTypes.INTEGER,
11 | primaryKey: true,
12 | autoIncrement: true
13 | },
14 | title: DataTypes.STRING,
15 | content: DataTypes.TEXT,
16 | created_at: DataTypes.DATE
17 | },
18 | {
19 | sequelize,
20 | modelName: 'posts',
21 | freezeTableName: true,
22 | hooks: {
23 | beforeCreate(instance) {
24 | instance.created_at = moment();
25 | }
26 | }
27 | }
28 | );
29 |
30 | return posts;
31 | };
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@babel/core": "7.7.4",
4 | "@babel/plugin-proposal-object-rest-spread": "7.7.4",
5 | "@babel/plugin-transform-async-to-generator": "7.7.4",
6 | "@babel/preset-env": "7.7.4",
7 | "@babel/preset-react": "7.7.4",
8 | "babel-loader": "8.0.6",
9 | "connect-redis": "4.0.4",
10 | "core-js": "3.6.5",
11 | "css-loader": "3.2.1",
12 | "express": "4.17.1",
13 | "express-session": "1.17.1",
14 | "less": "3.10.3",
15 | "less-loader": "5.0.0",
16 | "mini-css-extract-plugin": "0.8.0",
17 | "moment": "2.27.0",
18 | "mysql2": "2.1.0",
19 | "nocamel": "1.0.2",
20 | "nodemon": "2.0.4",
21 | "npm-run-all": "4.1.5",
22 | "react": "16.12.0",
23 | "react-dom": "16.12.0",
24 | "redis": "3.0.2",
25 | "regenerator-runtime": "0.13.3",
26 | "sequelize": "5.21.13",
27 | "style-loader": "1.0.1",
28 | "twig": "1.15.1",
29 | "webpack": "4.41.2",
30 | "webpack-cli": "3.3.10"
31 | },
32 | "scripts": {
33 | "start": "npm-run-all --parallel proc:webpack proc:app",
34 | "proc:webpack": "webpack --mode development --colors --watch",
35 | "proc:app": "nodemon --ignore frontend/ --ignore public/ -e js,twig app.js"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engineer-man/express-mvc-skeleton/cd4cfe99eebbaee89a70071dcef99808138945b5/public/.gitkeep
--------------------------------------------------------------------------------
/public/images/chase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engineer-man/express-mvc-skeleton/cd4cfe99eebbaee89a70071dcef99808138945b5/public/images/chase.png
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # simple express.js skeleton application
2 | good for somebody who isn't sure how it all fits together and needs a starting point for making web applications in node.js.
3 |
4 | #### it uses the following key components:
5 | * `express` for handling routes
6 | * `sequelize` as the database orm
7 | * `redis` for session store
8 | * `webpack` for bundling frontend assets
9 |
10 | #### quickstart
11 | ```shell
12 | git clone https://github.com/engineer-man/express-mvc-skeleton
13 | cd express-mvc-skeleton
14 | docker-compose up
15 | ```
16 | then open http://127.0.0.1:8000 in your browser
17 |
18 | #### other essential commands
19 | ```shell
20 | # access container running your app
21 | docker-compose exec app /bin/bash
22 |
23 | # access mysql
24 | docker-compose exec mysql mysql -uroot -proot
25 | ```
26 | #### outline of project structure
27 | ```
28 | |- controllers # these are for handling routes
29 | |- frontend # contains js, jsx, less files to be bundled for the frontend
30 | |- models # these are for accessing your db
31 | |- public # all non-routes get served from here
32 | |- var # variable data for variable purposes
33 | |- views # these are your templates, controllers will render them
34 | ```
35 |
--------------------------------------------------------------------------------
/var/docker/mysql/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engineer-man/express-mvc-skeleton/cd4cfe99eebbaee89a70071dcef99808138945b5/var/docker/mysql/.gitkeep
--------------------------------------------------------------------------------
/var/docker/redis/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engineer-man/express-mvc-skeleton/cd4cfe99eebbaee89a70071dcef99808138945b5/var/docker/redis/.gitkeep
--------------------------------------------------------------------------------
/var/sample_schema.sql:
--------------------------------------------------------------------------------
1 | create database if not exists testdb;
2 |
3 | use testdb;
4 |
5 | create table posts (
6 | post_id int unsigned not null auto_increment,
7 | title varchar(128) not null,
8 | content text not null,
9 | created_at datetime not null,
10 | primary key (post_id)
11 | ) character set utf8mb4 collate utf8mb4_unicode_ci;
12 |
13 | insert into posts values (default, 'First Title', 'First Content', now());
14 | insert into posts values (default, 'Second Title', 'Second Content', now());
15 |
--------------------------------------------------------------------------------
/views/home/home.twig:
--------------------------------------------------------------------------------
1 | {% extends 'master.twig' %}
2 | {% block content %}
3 |
4 |
5 | {% for post in posts %}
6 |
8 | {{ post.content }} 9 |
10 | {% endfor %} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /views/master.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |