We are going to create a photo sharing service where the user is able to upload pictures and like them.
We will be using a simple MVC, so our file structure will be:
/controllers
/photos.js
/models
/photos.js
/views
/index.pug
/layout.pug
/public
/images
/1.jpg
/2.jpg
...
/style.css
/.env
/.gitignore
/app.js
/package.json
/Procfile
/routes.js
So far there will be no user auth as it'd take too long to explain it all. We will need to install few packages within our web folder, which will be explained as we go:
npm install auto-load body-parser cloudinary dotenv express formidable mongoose pug --save
A small reminder about requests flow; when the user requests any page in any way, it will reach the app.js. In that file we will have all of our server initial setup, and then the flow will continue with our router. From the router we specify what call goes where.
So we first set up our app.js:
// Load the environment configuration from ".env"
require('dotenv').config();
// Import the modules that we will use in this file
var express = require('express');
var mongoose = require('mongoose');
var routes = require('./routes');
// Server configuration
var app = express();
app.set('view engine', 'pug'); // The "html engine"
app.use(express.static('public')); // Where the static files are
// Connect to the database before accepting any request
mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;
db.once('open', function(){
// Use the routes stored in ./routes.js
app.use(routes);
// Actually start listening to the requests
app.listen(process.env.PORT || 3000);
});
In routes.js:
var express = require('express');
var router = express.Router();
var controllers = require('auto-load')('controllers');
router.get('/', controllers.photos.index);
module.exports = router;
Let's mock the data:
module.exports.index = function(req, res){
res.render('index', {
photos: [
'/images/1.jpg',
'/images/2.jpg',
'/images/3.jpg',
'/images/4.jpg',
'/images/5.jpg'
]
});
}
Install the dependencies
npm install formidable cloudinary --save
Then in our controllers/photos.js:
var formidable = require('formidable');
var cloudinary = require('cloudinary');
cloudinary.config({
cloud_name: process.env.cloud,
api_key: process.env.key,
api_secret: process.env.secret
});
exports.upload = function(req, res, next){
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
if (err) next(err);
cloudinary.uploader.upload(files.image.path, function(result) {
// Let's force https in the image:
var image = result.url.replace('http://', 'https://');
res.redirect('/');
});
});
});
Our layout.pug:
doctype html
html
head
title= title
link(rel="stylesheet" href="https://cdn.jsdelivr.net/picnicss/6.0.0/plugins.min.css")
link(rel='stylesheet', href='style.css')
block head
body
block content
script.
document.addEventListener("DOMContentLoaded", function() {
[].forEach.call(document.querySelectorAll('.dropimage'), function(img){
img.onchange = function(e){
var inputfile = this, reader = new FileReader();
reader.onloadend = function(){
inputfile.style['background-image'] = 'url('+reader.result+')';
}
reader.readAsDataURL(e.target.files[0]);
}
});
});
In index.pug:
extends layout
block content
h1= name
div.flex.one.two-500.five-900
each photo in photos
div.photo
img(src=photo)
button.like Like
form(action="/photos" method="POST" enctype="multipart/form-data")
div.flex.two
div
label.dropimage
input(type="file" name="image" title="Drop image or click me")
div
label Title:
input(name="title" placeholder="Title")
label Nickname:
input(name="nick" placeholder="Nickname")
input(type="submit" value="Upload")
In our style.css:
body {
width: 100%;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
img {
width: 100%;
}
New photos.js:
var models = require('auto-load')('models');
var formidable = require('formidable');
var cloudinary = require('cloudinary');
cloudinary.config({
cloud_name: process.env.cloud,
api_key: process.env.key,
api_secret: process.env.secret
});
module.exports.index = function(req, res){
models.photos.find({}, function(err, images){
res.render('index', { photos: images });
});
}
module.exports.upload = function(req, res, next){
var form = new formidable.IncomingForm();
function redirect(err){
if (err) next(err);
res.redirect('/');
}
function parseForm(err, fields, files) {
if (err) next(err);
cloudinary.uploader.upload(files.image.path, function (result) {
// Let's force https in the image:
var url = result.url.replace('http://', 'https://');
var Photo = new models.photos({
url: url,
title: fields.title,
nickname: fields.nick,
});
Photo.save(redirect);
});
}
form.parse(req, parseForm);
}
The model.js:
var mongoose = require('mongoose');
var photosSchema = mongoose.Schema({
title: { type: String, required: true },
nickname: { type: String, unique: true, required: true },
url: { type: String, required: true },
likes: [{ type: String }]
});
module.exports = mongoose.model('Photos', photosSchema);
Auth
We will follow Auth0 tutorial.