├── .gitignore ├── public ├── stylesheets │ ├── style.css │ ├── main.css │ └── style.css.map ├── javascripts │ └── articles.js └── sass │ └── style.sass ├── views ├── pages │ ├── contact.pug │ └── index.pug ├── articles │ ├── show.pug │ ├── index.pug │ ├── create.pug │ └── update.pug ├── index.pug ├── partial │ ├── search.pug │ ├── footer.pug │ └── header.pug ├── layout.pug └── auth │ └── login.pug ├── routes ├── auth.js └── articles.js ├── models ├── config.json ├── Categories.js ├── index.js └── Articles.js ├── controllers ├── AuthController.js └── ArticlesController.js ├── package.json ├── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/javascripts/articles.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/sass/style.sass: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px 3 | } -------------------------------------------------------------------------------- /public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | form#search { 2 | flex-flow: row wrap; 3 | } 4 | -------------------------------------------------------------------------------- /views/pages/contact.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | h1="Page Contact" -------------------------------------------------------------------------------- /views/articles/show.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | h1=`Voir l'article ${article.title} ` -------------------------------------------------------------------------------- /views/pages/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | h1="Page Accueil" 5 | a(href="contact") Page Contact -------------------------------------------------------------------------------- /views/index.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1="Hello Widlers" 5 | a(href="articles/liste") Lien vers articles -------------------------------------------------------------------------------- /views/partial/search.pug: -------------------------------------------------------------------------------- 1 | form#search.form-inline 2 | input.form-control(type='search', placeholder='Rechercher un article') 3 | button.btn.btn-outline-success.my-2.my-sm-0(type='submit') Rechercher 4 | -------------------------------------------------------------------------------- /public/stylesheets/style.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "style.css", 4 | "sources": [ 5 | "style.sass" 6 | ], 7 | "names": [], 8 | "mappings": "AAAA,AAAA,IAAI,CAAC;EACH,OAAO,EAAE,IAAI;EACb,IAAI,EAAE,kDAAkD,GAAI;;AAE9D,AAAA,CAAC,CAAC;EACA,KAAK,EAAE,OAAO,GAAI" 9 | } -------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | /** 5 | * Routing for Auth 6 | */ 7 | const AuthController = require("../controllers/AuthController"); 8 | const controller = new AuthController(); 9 | 10 | router.get("/login", (req, res) => controller.login(req, res)); 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /views/partial/footer.pug: -------------------------------------------------------------------------------- 1 | footer.text-muted 2 | .container 3 | p.float-right 4 | a(href='#') Back to top 5 | p Album example is © Bootstrap, but please download and customize it for yourself! 6 | p 7 | | New to Bootstrap? 8 | a(href='../../') Visit the homepage 9 | | or read our 10 | a(href='../../getting-started/') getting started guide 11 | | . 12 | -------------------------------------------------------------------------------- /models/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "username": "root", 4 | "password": "root", 5 | "database": "blog", 6 | "port": 8889, 7 | "host": "127.0.0.1", 8 | "dialect": "mysql", 9 | "logging": false 10 | }, 11 | "production": { 12 | "username": "root", 13 | "password": null, 14 | "database": "database_production", 15 | "host": "127.0.0.1", 16 | "dialect": "mysql" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /controllers/AuthController.js: -------------------------------------------------------------------------------- 1 | const db = require(`../models/index.js`); 2 | /** 3 | * Class Auth Controller 4 | */ 5 | class AuthController { 6 | /** 7 | * Page of Login 8 | * @param {*} req 9 | * @param {*} res 10 | */ 11 | login(req, res) { 12 | if (req.session.views) { 13 | req.session.views++; 14 | } else { 15 | req.session.views = 123; 16 | } 17 | res.render("auth/login"); 18 | } 19 | } 20 | 21 | module.exports = AuthController; 22 | -------------------------------------------------------------------------------- /views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css') 6 | link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css") 7 | link(rel="stylesheet", href="/stylesheets/main.css") 8 | body 9 | include partial/header.pug 10 | div(class="container") 11 | block content 12 | script(src="/javascripts/articles.js") 13 | //- include partial/footer.pug 14 | -------------------------------------------------------------------------------- /models/Categories.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Articles 3 | * @param {*} sequelize 4 | * @param {*} DataTypes 5 | */ 6 | const Articles = require("./Articles"); 7 | 8 | const Categories = (sequelize, DataTypes) => { 9 | return sequelize.define( 10 | "Categories", // name of Model 11 | { 12 | // fields 13 | title: DataTypes.STRING, 14 | description: DataTypes.STRING 15 | }, 16 | { 17 | classMethods: { 18 | associate: models => { 19 | Categories.hasMany(models.Articles); 20 | } 21 | } 22 | } 23 | ); 24 | }; 25 | 26 | module.exports = Categories; 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6express", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon app.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.18.2", 14 | "cookie-parser": "^1.4.3", 15 | "debug": "^3.1.0", 16 | "express": "^4.16.3", 17 | "express-session": "^1.15.6", 18 | "morgan": "^1.9.0", 19 | "mysql2": "^1.5.3", 20 | "node-sass-middleware": "^0.11.0", 21 | "pug": "^2.0.3", 22 | "sequelize": "^4.37.4", 23 | "validator": "^9.4.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /views/auth/login.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | p=session.views 5 | form(action="/login", method="POST") 6 | .form-group 7 | label(for='exampleInputEmail1') Email address 8 | input#exampleInputEmail1.form-control(type='email', aria-describedby='emailHelp', placeholder='Enter email') 9 | small#emailHelp.form-text.text-muted We'll never share your email with anyone else. 10 | .form-group 11 | label(for='exampleInputPassword1') Password 12 | input#exampleInputPassword1.form-control(type='password', placeholder='Password') 13 | .form-check 14 | input#exampleCheck1.form-check-input(type='checkbox') 15 | label.form-check-label(for='exampleCheck1') Check me out 16 | button.btn.btn-primary(type='submit') Connexion 17 | -------------------------------------------------------------------------------- /routes/articles.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | /** 5 | * Routing for Articles 6 | */ 7 | const ArticlesController = require("../controllers/ArticlesController"); 8 | const controller = new ArticlesController(); 9 | 10 | router.get("/liste", (req, res) => controller.liste(req, res)); 11 | router.get("/create", (req, res) => controller.create(req, res)); 12 | router.post("/store", (req, res) => controller.store(req, res)); 13 | router.post("/store/:id", (req, res) => controller.upgrade(req, res)); 14 | router.get("/update/:id", (req, res) => controller.update(req, res)); 15 | router.get("/remove/:id", (req, res) => controller.remove(req, res)); 16 | router.get("/visible/:id", (req, res) => controller.visible(req, res)); 17 | router.get("/invisible/:id", (req, res) => controller.invisible(req, res)); 18 | router.get("/:id", (req, res) => controller.show(req, res)); 19 | 20 | module.exports = router; 21 | -------------------------------------------------------------------------------- /models/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Sequelize = require("sequelize"); 4 | const basename = path.basename(module.filename); 5 | const env = process.env.NODE_ENV || "development"; 6 | const config = require(__dirname + "/config.json")[env]; 7 | const db = {}; 8 | 9 | const sequelize = new Sequelize( 10 | config.database, 11 | config.username, 12 | config.password, 13 | config 14 | ); 15 | 16 | // read sync files 17 | fs 18 | .readdirSync(__dirname) 19 | .filter( 20 | file => 21 | file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js" 22 | ) 23 | .forEach(file => { 24 | const model = sequelize.import(path.join(__dirname, file)); 25 | db[model.name] = model; 26 | }); 27 | 28 | //associate 29 | Object.keys(db).forEach(modelName => { 30 | if (db[modelName].associate) { 31 | db[modelName].associate(db); 32 | } 33 | }); 34 | 35 | db.sequelize = sequelize; 36 | db.Sequelize = Sequelize; 37 | 38 | module.exports = db; 39 | -------------------------------------------------------------------------------- /views/partial/header.pug: -------------------------------------------------------------------------------- 1 | header 2 | #navbarHeader.collapse.bg-dark 3 | .container 4 | .row 5 | .col-sm-8.col-md-7.py-4 6 | h4.text-white Blog 7 | p.text-muted 8 | | Add some information about the album below, the author, or any other background context. Make it a few sentences long so folks can pick up some informative tidbits. Then, link them off to some social networking sites or contact information. 9 | .col-sm-4.offset-md-1.py-4 10 | h4.text-white Contact 11 | ul.list-unstyled 12 | li 13 | a.text-white(href='#') Follow on Twitter 14 | li 15 | a.text-white(href='#') Like on Facebook 16 | li 17 | a.text-white(href='#') Email me 18 | .navbar.navbar-dark.bg-dark.box-shadow 19 | .container.d-flex.justify-content-between 20 | a.navbar-brand.d-flex.align-items-center(href='#') 21 | a(href="/articles/liste") Blog 22 | strong 23 | button.navbar-toggler(type='button', data-toggle='collapse', data-target='#navbarHeader', aria-controls='navbarHeader', aria-expanded='false', aria-label='Toggle navigation') 24 | span.navbar-toggler-icon 25 | -------------------------------------------------------------------------------- /models/Articles.js: -------------------------------------------------------------------------------- 1 | const Categories = require("./Categories"); 2 | /** 3 | * Model Articles 4 | * @param {*} sequelize 5 | * @param {*} DataTypes 6 | */ 7 | const Articles = (sequelize, DataTypes) => { 8 | const Articles = sequelize.define( 9 | "Articles", // name of Model 10 | { 11 | // fields 12 | title: { type: DataTypes.STRING, unique: "theTitle", notEmpty: true }, 13 | description: { type: DataTypes.TEXT, is: ["^[a-z]{10,}$", "i"] }, 14 | active: { type: DataTypes.INTEGER }, 15 | datePublication: { type: DataTypes.DATE, isDate: true }, 16 | note: { type: DataTypes.INTEGER, isInt: true, min: 1, max: 5 }, 17 | category_id: { type: DataTypes.INTEGER } 18 | }, 19 | { 20 | getterMethods: { 21 | dateFr() { 22 | function pad(s) { 23 | return s < 10 ? "0" + s : s; 24 | } 25 | var d = new Date(this.datePublication); 26 | return [ 27 | pad(d.getDate()), 28 | pad(d.getMonth() + 1), 29 | d.getFullYear() 30 | ].join("/"); 31 | } 32 | } 33 | }, 34 | { 35 | classMethods: { 36 | associate: models => { 37 | Articles.belongsTo(models.Categories, { 38 | as: "category" 39 | }); 40 | } 41 | } 42 | } 43 | ); 44 | return Articles; 45 | }; 46 | 47 | module.exports = Articles; 48 | -------------------------------------------------------------------------------- /views/articles/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | include ../partial/search.pug 5 | h1="Liste des articles" 6 | table.table 7 | thead 8 | tr 9 | th(scope='col') # 10 | th(scope='col') Title 11 | th(scope='col') Description 12 | th(scope='col') Date de publication 13 | th(scope='col') Note 14 | th(scope='col') Visible 15 | th(scope='col') Action 16 | tbody 17 | each elt, index in articles 18 | tr 19 | td 20 | a(href=elt.id) #{elt.id} 21 | td 22 | a(href=elt.id) #{elt.title} 23 | td=elt.description 24 | td=(elt.dateFr) 25 | td=(elt.note) 26 | td 27 | if elt.active === true 28 | i.fa.fa-eye 29 | else 30 | i.fa.fa-eye-slash 31 | td 32 | if elt.active === 1 33 | a(href=`invisible/${elt.id}`, class="btn btn-danger") 34 | i.glyphicon.glyphicon-pencil 35 | | Invisible 36 | else 37 | a(href=`visible/${elt.id}`, class="btn btn-success") 38 | i.glyphicon.glyphicon-pencil 39 | | Visible 40 | a(href=`update/${elt.id}`, class="btn btn-warning") 41 | i.glyphicon.glyphicon-pencil 42 | | Modifier 43 | a(href=`remove/${elt.id}`, class="btn btn-danger") 44 | i.glyphicon.glyphicon-minus 45 | | Supprimer 46 | a(href="create", class="btn btn-primary") 47 | i.glyphicon.glyphicon-plus 48 | | Créer un article -------------------------------------------------------------------------------- /views/articles/create.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | h1="Page créer un article" 5 | form( method='post', action='store') 6 | .form-group 7 | label(for='title') Titre 8 | input#title.form-control(type='text', name='title', placeholder='Un joli titre') 9 | .form-group 10 | label(for='description') Description 11 | textarea#description.form-control(name='description',placeholder='Une jolie description') 12 | .form-group 13 | label(for='date') Date de publication 14 | input#datePublication.form-control(type='date', name='datePublication', placeholder='dd/mm/YYYY') 15 | 16 | .form-check-inline 17 | input#exampleRadios1.form-check-input(type='radio', name='note', value='1') 18 | label.form-check-label(for='exampleRadios1') 19 | | 1 20 | .form-check-inline 21 | input#exampleRadios2.form-check-input(type='radio', name='note', value='2') 22 | label.form-check-label(for='exampleRadios2') 23 | | 2 24 | .form-check-inline 25 | input#exampleRadios3.form-check-input(type='radio', name='note', value='3', checked) 26 | label.form-check-label(for='exampleRadios3') 27 | | 3 28 | .form-check-inline 29 | input#exampleRadios4.form-check-input(type='radio', name='note', value='4') 30 | label.form-check-label(for='exampleRadios4') 31 | | 4 32 | .form-check-inline 33 | input#exampleRadios5.form-check-input(type='radio', name='note', value='5') 34 | label.form-check-label(for='exampleRadios5') 35 | | 5 36 | 37 | .form-group 38 | label(for='category') Category 39 | select#category.form-control(name='category_id') 40 | option(value="1") Actu 41 | option(value="2") Sport 42 | option(value="3") Technologie 43 | option(value="4") Economie 44 | 45 | .form-check 46 | input#active.form-check-input.position-static(type='checkbox', value='1',name="active") 47 | label.form-check-label(for='active') 48 | | Artile actif ? 49 | button.btn.btn-primary(type='submit') Créer un article 50 | -------------------------------------------------------------------------------- /views/articles/update.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | h1="Modifier un article" 5 | form( method='post', action=`/articles/store/${article.id}`) 6 | .form-group 7 | label(for='title') Titre 8 | input#title.form-control(type='text',value=article.title, name='title', placeholder='Un joli titre') 9 | .form-group 10 | label(for='description') Description 11 | textarea#description.form-control(name='description', placeholder='Une jolie description') #{article.description} 12 | 13 | .form-check-inline 14 | input#exampleRadios1.form-check-input(type='radio', name='note', value='1',checked=article.note == 1) 15 | label.form-check-label(for='exampleRadios1') 16 | | 1 17 | .form-check-inline 18 | input#exampleRadios2.form-check-input(type='radio', name='note', value='2',checked=article.note == 2) 19 | label.form-check-label(for='exampleRadios2') 20 | | 2 21 | .form-check-inline 22 | input#exampleRadios3.form-check-input(type='radio', name='note', value='3',checked=article.note == 3) 23 | label.form-check-label(for='exampleRadios3') 24 | | 3 25 | .form-check-inline 26 | input#exampleRadios4.form-check-input(type='radio', name='note', value='4',checked=article.note == 4) 27 | label.form-check-label(for='exampleRadios4') 28 | | 4 29 | .form-check-inline 30 | input#exampleRadios5.form-check-input(type='radio', name='note', value='5',checked=article.note == 5) 31 | label.form-check-label(for='exampleRadios5') 32 | | 5 33 | .form-group 34 | label(for='category') Category 35 | select#category.form-control(name='category_id') 36 | option(value="1", selected=article.category_id == 1) Actu 37 | option(value="2", selected=article.category_id == 2) Sport 38 | option(value="3", selected=article.category_id == 3) Technologie 39 | option(value="4", selected=article.category_id == 4) Economie 40 | 41 | .form-check 42 | input#active.form-check-input.position-static(type='checkbox',name="active", checked=article.active == 1) 43 | label.form-check-label(for='active') 44 | | Artile actif ? 45 | button.btn.btn-primary(type='submit') Créer un article 46 | -------------------------------------------------------------------------------- /controllers/ArticlesController.js: -------------------------------------------------------------------------------- 1 | const db = require(`../models/index.js`); 2 | /** 3 | * Class Articles Controller 4 | */ 5 | class ArticlesController { 6 | /** 7 | * Liste of Articles 8 | * @param {*} req 9 | * @param {*} res 10 | */ 11 | liste(req, res) { 12 | db.Articles.findAll().then(articles => 13 | res.render("articles/index", { articles }) 14 | ); 15 | } 16 | /** 17 | * Crea te a article Form 18 | * @param {*} req 19 | * @param {*} res 20 | */ 21 | create(req, res) { 22 | return res.render("articles/create"); 23 | } 24 | /** 25 | * Store a new article in database 26 | * @param {*} req 27 | * @param {*} res 28 | */ 29 | store(req, res) { 30 | db.Articles.create(req.body).then(article => res.redirect("liste")); 31 | } 32 | 33 | /** 34 | * Update a article 35 | * @param {*} req 36 | * @param {*} res 37 | */ 38 | update(req, res) { 39 | const id = req.params.id; 40 | db.Articles.findById(id).then(article => 41 | res.render("articles/update", { article }) 42 | ); 43 | } 44 | /** 45 | * Upgrade a article in database 46 | * @param {*} req 47 | * @param {*} res 48 | */ 49 | upgrade(req, res) { 50 | const id = req.params.id; 51 | db.Articles.update(req.body, { where: { id } }).then(article => 52 | res.redirect("/articles/liste") 53 | ); 54 | } 55 | /** 56 | * Remove an article in database 57 | * @param {*} req 58 | * @param {*} res 59 | */ 60 | remove(req, res) { 61 | const id = req.params.id; 62 | db.Articles.destroy({ 63 | where: { id } 64 | }).then(() => res.redirect("/articles/liste")); 65 | } 66 | 67 | /** 68 | * Show a article 69 | * @param {*} req 70 | * @param {*} res 71 | */ 72 | show(req, res) { 73 | const id = req.params.id; 74 | db.Articles.findById(id).then(article => 75 | res.render("articles/show", { article }) 76 | ); 77 | } 78 | 79 | /** 80 | * Render visible 81 | * @param {*} req 82 | * @param {*} res 83 | */ 84 | visible(req, res) { 85 | const id = req.params.id; 86 | db.Articles.findById(id).then(article => 87 | article 88 | .update({ active: 1 }) 89 | .then(result => res.redirect("/articles/liste")) 90 | ); 91 | } 92 | 93 | /** 94 | * Render invisible an article 95 | * @param {*} req 96 | * @param {*} res 97 | */ 98 | invisible(req, res) { 99 | const id = req.params.id; 100 | db.Articles.findById(id).then(article => 101 | article 102 | .update({ active: 0 }) 103 | .then(result => res.redirect("/articles/liste")) 104 | ); 105 | } 106 | } 107 | 108 | module.exports = ArticlesController; 109 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************************************* 2 | * ************************************************************************* 3 | * ************************************************************************* 4 | * Configuration of Frameworks 5 | * ************************************************************************* 6 | * ************************************************************************* 7 | ******************************************************************************************************************************************/ 8 | 9 | const express = require("express"); 10 | const Debug = require("debug"); 11 | const path = require("path"); 12 | const app = express(); 13 | const port = 3000; 14 | const debug = Debug("express:app"); // Module for Debug 15 | const logger = require("morgan"); // Module for Log 16 | const bodyParser = require("body-parser"); // Module for POST/GET datas 17 | const cookieParser = require("cookie-parser"); // Module for cookie in Session 18 | const sassMiddleware = require("node-sass-middleware"); 19 | const session = require("express-session"); 20 | 21 | app.use(express.static(__dirname + "/public")); // all statics files in /public 22 | app.set("views", path.join(__dirname, "views")); 23 | app.set("view engine", "pug"); 24 | 25 | app.use(logger("dev")); 26 | app.use(bodyParser.json()); // API response en JSON 27 | app.use( 28 | // donnée en get post non encodé par l'URL 29 | bodyParser.urlencoded({ 30 | extended: true 31 | }) 32 | ); 33 | 34 | /** 35 | * Configuration of Session 36 | */ 37 | app.set("trust proxy", 1); // trust first proxy 38 | app.use( 39 | session({ 40 | secret: "*****JeSuisLaClefSecrèteWild2018*****", 41 | resave: false, // Forces the session to be saved back to the session store, even if the session was never modified during the request. 42 | cookie: { maxAge: 60000 }, // lifetime of cookie 43 | saveUninitialized: false // Forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified. 44 | }) 45 | ); 46 | /** 47 | * Store in local session all 48 | */ 49 | app.use((req, res, next) => { 50 | res.locals.session = req.session; 51 | next(); 52 | }); 53 | 54 | /********************************************************************************************************************************************* 55 | * ************************************************************************* 56 | * ************************************************************************* 57 | * Routing 58 | * ************************************************************************* 59 | * ************************************************************************* 60 | ******************************************************************************************************************************************/ 61 | 62 | /** 63 | * Routing 64 | */ 65 | const articles = require("./routes/articles"); 66 | 67 | app.get("/", (req, res) => res.render("index")); 68 | app.use("/articles", articles); 69 | 70 | const auth = require("./routes/auth"); 71 | app.use("/auth", auth); 72 | 73 | app.listen(port, () => { 74 | console.log("Example app listening on port port!"); 75 | }); 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Awesome Express [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) 2 | 3 | [](http://expressjs.com) 4 | 5 | Curated list of express.js resources 6 | 7 | ### Rendering a template 8 | 9 | https://gist.github.com/joepie91/c0069ab0e0da40cc7b54b8c2203befe1 10 | 11 | ## Contents 12 | 13 | * [General](#general) 14 | * [Videos](#videos) 15 | * [Books](#books) 16 | * [Articles](#articles) 17 | * [Courses](#courses) 18 | * [Tools](#tools) 19 | * [Middleware](#middleware) 20 | * [Security](#security) 21 | * [Addons](#addons) 22 | 23 | ## General 24 | 25 | * [Official website](http://expressjs.com) - official website for express.js 26 | * [GitHub repository](https://github.com/expressjs/express) - express.js GitHub repository 27 | * [Express workshop](https://github.com/azat-co/expressworks) - workshop built on top of [workshopper](https://github.com/workshopper/workshopper) 28 | 29 | ## Videos 30 | 31 | * [Tutorial series by Code School](https://www.youtube.com/watch?v=IjXAr5CJ2Ec) - code School tutorial covering express fundamentals 32 | * [Tutorial by Derek Banas](https://www.youtube.com/watch?v=xDCKcNBFsuI) - 1 hour tutorial by Derek Banas 33 | 34 | ## Books 35 | 36 | * [Express in Action](https://www.manning.com/books/express-in-action) 37 | * [Web Development with Node and Express](http://shop.oreilly.com/product/0636920032977.do) 38 | * [Express Web Application Developement](http://shop.oreilly.com/product/9781849696548.do) 39 | * [Express.js Blueprints](https://www.amazon.com/Express-js-Blueprints-Ben-Augarten-ebook/dp/B00XPMJF1U) 40 | 41 | ## Articles 42 | 43 | * [Learn to Use the New Router in ExpressJS 4.0](https://scotch.io/tutorials/learn-to-use-the-new-router-in-expressjs-4) - express routing tutorial 44 | * [Simple server side cache for Express.js with Node.js](https://medium.com/the-node-js-collection/simple-server-side-cache-for-express-js-with-node-js-45ff296ca0f0) - simple caching for express.js 45 | 46 | ## Courses 47 | 48 | * [ExpressJS fundamentals](https://www.udemy.com/expressjs-fundamentals/) - udemy course with fundamentals of express.js 49 | * [Projects in ExpressJS - Learn ExpressJs building 10 projects](https://www.udemy.com/projects-in-expressjs-learn-expressjs-building-10-projects/) - hands on tutorial with express.js on udemy 50 | * [Building blocks of Express.js](https://www.codeschool.com/courses/building-blocks-of-express-js) - course from Code School 51 | * [Curated ExpressJS Courses](https://hackr.io/tutorials/learn-express-js) - a list of community curated resources 52 | 53 | ## Tools 54 | 55 | * [Express application generator](https://expressjs.com/en/starter/generator.html) - a generator to scaffold the basic structure of a express project 56 | * [Express mvc generator](https://github.com/rajikaimal/express-mvc) - modified express generator with MVC architecture 57 | * [Yeoman express generator - generator-express](https://github.com/petecoop/generator-express) - a yeoman generator 58 | 59 | ## Middleware 60 | 61 | * [Expressa](https://github.com/thomas4019/expressa) - express middleware for easily making REST apis 62 | * [morgan](https://github.com/expressjs/morgan) - log each request 63 | * [cors](https://github.com/expressjs/cors) - enable CORS 64 | * [body-parser](https://github.com/expressjs/body-parser) - node.js body parsing middleware 65 | * [multer](https://github.com/expressjs/multer) - node.js middleware for handling `multipart/form-data` 66 | * [session](https://github.com/expressjs/session) - simple session middleware for Express 67 | * [errorhandler](https://github.com/expressjs/errorhandler) - development-only error handler middleware 68 | * [serve-favicon](https://github.com/expressjs/serve-favicon) - favicon serving middleware 69 | * [csurf](https://github.com/expressjs/csurf) - node.js CSRF protection middleware 70 | * [Passport](http://www.passportjs.org) - Simple, unobtrusive authentication 71 | * [Merror](https://github.com/mamsoudi/merror) - A RESTful-friendly Express Middleware for HTTP error handling and error responses 72 | 73 | ## Microservices 74 | 75 | * [ExpressGateway](https://github.com/ExpressGateway/express-gateway) - a microservices API Gateway built on top of ExpressJS 76 | 77 | ## Security 78 | 79 | * [Security best practices](https://expressjs.com/en/advanced/best-practice-security.html) - production Best Practices: Security 80 | * [Security tips from NodeSource](https://nodesource.com/blog/nine-security-tips-to-keep-express-from-getting-pwned/) - 9 Security Tips to Keep Express from Getting Pwned 81 | * [Helmet.js](https://github.com/helmetjs/helmet) - help secure Express apps with various HTTP headers 82 | 83 | ## Addons 84 | 85 | * [Kraken.js](http://krakenjs.com) - kraken is a secure and scalable layer that extends express by providing structure and convention 86 | 87 | ## License 88 | 89 | [![CC0](http://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg)](https://creativecommons.org/publicdomain/zero/1.0/) 90 | 91 | To the extent possible under law, [Rajika Imal](https://rajikaimal.github.io) has waived all copyright and related or neighboring rights to this work. 92 | --------------------------------------------------------------------------------