Skip to content

Instantly share code, notes, and snippets.

@igorlima igorlima/Procfile
Last active Sep 14, 2017

Embed
What would you like to do?
Artigo Tableless - Como publicar aplicação NodeJS no Heroku

Gittip Donate Button

Neste artigo vou utilizar uma aplicação single page para demonstrar passo a passo as etapas necessárias para publicar uma aplicação no Heroku. O código da aplicação de exemplo está disponível em um gist, para baixá-lo digite o comando:

git clone gist@gist.github.com:69153705256f6a9a4557.git minhas-midias-sociais

Dentro da pasta minhas midias sociais, o arquivo index.html pode ser aberto utilizando qualquer navegador. Como são arquivos estáticos, será possível visualizar a aplicação web normalmente.

Para rodar esse pequeno projeto no serviço de cloud, será preciso criar um servidor para tal. Nesse caso, vamos usar o Express, framework para NodeJS inspirado no Sinatra. O código está no arquivo server.js. Para rodá-lo execute:

npm install
node server.js

Para confirmar que tudo está funcionando como o esperado, acesse o endereço http://localhost:5000 e pingo, já temos uma aplicação NodeJS para ser hospedada na nuvem.

Agora, vá ao site Heroku e faça os seguintes passos: (i) se cadastre, (ii) crie uma aplicação e (iii) carregue sua chave pública ssh na sessão SSH Keys. Caso precise de detalhes de como gerar uma chave ssh, acesse o link.

Heroku possui um mecanismo para declarar qual comando deve ser executado para iniciar um serviço na nuvem: no nosso caso, o script node server.js. Isso deve ser declarado no arquivo de texto Procfile. Após esses passos, tudo está pronto para a nossa primeira publicação na nuvem. Digite o script abaixo no diretório do projeto e a mágica estará feita:

git remote add heroku git@heroku.com:o-nome-da-aplicacao-criada-no-heroku.git
git push heroku HEAD:master

Após o push, sua aplicação pode ser visualizada no endereço http:/o-nome-da-aplicacao-criada-no-heroku.herokuapp.com/, conforme ilustra imagem abaixo. E é isso, pessoal. Espero que tenha gostado. Até a próxima.

Minhas midias sociais

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Minhas mídias sociais</title>
<!-- Sets initial viewport load and disables zooming -->
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<!-- Makes your prototype chrome-less once bookmarked to your phone's home screen -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- Include CSS -->
<link href="http://cdnjs.cloudflare.com/ajax/libs/ratchet/2.0.2/css/ratchet.min.css" rel="stylesheet">
<link href="http://cdnjs.cloudflare.com/ajax/libs/ratchet/2.0.2/css/ratchet-theme-ios.min.css" rel="stylesheet">
<link href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css" rel="stylesheet">
<link href="main.css" rel="stylesheet"/>
</head>
<body class="social-media">
<!-- Make sure all your bars are the first things in your <body> -->
<header class="bar bar-nav">
<h1 class="title">Minhas Midias Sociais</h1>
</header>
<!-- Wrap all non-bar HTML in the .content div (this is actually what scrolls) -->
<div class="content">
<ul class="table-view videos" data-bind="foreach: dataFromYoutube, visible: isYoutubeVisible">
<li class="table-view-cell video">
<div data-bind="visible: videos.length > 1, click: goPrevious">
<span class="navigate-left"></span>
<span class="badge left" data-bind="text: previous"></span>
</div>
<center>
<div class="playlist-name" data-bind="text: title"></div>
<div data-bind="foreach: videos" >
<iframe type="text/html" data-bind="attr: { src: src }, visible: visible" width="204" src="" allowfullscreen frameborder="0" ></iframe>
</div>
</center>
<div data-bind="visible: videos.length > 1, click: goNext">
<span class="navigate-right"></span>
<span class="badge right" data-bind="text: next"></span>
</div>
<div class="video-divider-bar"></div>
</li>
</ul>
<ul class="table-view" data-bind="foreach: dataFromFacebook, visible: isFacebookVisible">
<li class="table-view-cell">
<div data-bind="if: isLink">
<span data-bind="text: message"></span><br>
<a data-bind="attr: { href: link, title: description }, text: name"></a>
</div>
<div data-bind="if: isStatus">
<span data-bind="text: message"></span>
<span data-bind="text: story"></span>
</div>
<div data-bind="if: isPhoto">
<span data-bind="text: message"></span><br>
<img data-bind="attr: { src: picture, alt: message }">
</div>
<div data-bind="if: isQuestion">
<span data-bind="text: story"></span>
</div>
<span><i data-bind="text: date"></i></span>
</li>
</ul>
<ul class="table-view" data-bind="foreach: dataFromTwitter, visible: isTwitterVisible">
<li class="table-view-cell">
<span data-bind="text: tweet"></span><br>
<span><i data-bind="text: date"></i></span>
</li>
</ul>
<ul class="table-view" data-bind="foreach: dataFromInstagram, visible: isInstagramVisible">
<li class="table-view-cell">
<center>
<a data-bind="attr: { href: link }">
<img src="" alt="" data-bind="attr: { src: images.thumbnail.url }">
</a>
<br>
<span><i data-bind="text: date"></i></span>
</center>
</li>
</ul>
</div>
<nav class="bar bar-tab">
<a class="tab-item" href="#" data-bind="click: updateFacebook, clickBubble: false, css: { active: isFacebookVisible }">
<span class="icon"><i class="fa fa-facebook" data-bind="css: { 'fa-facebook': isFacebookReady | !isFacebookLoading(), 'fa-spinner': isFacebookLoading, 'fa-spin': isFacebookLoading }"></i></span>
<span class="tab-label">Facebook</span>
</a>
<a class="tab-item" href="#" data-bind="click: updateInstagram, clickBubble: false, css: { active: isInstagramVisible }">
<span class="icon"><i class="fa fa-instagram" data-bind="css: { 'fa-instagram': isInstagramReady | !isInstagramLoading(), 'fa-spinner': isInstagramLoading, 'fa-spin': isInstagramLoading }"></i></span>
<span class="tab-label">Instagram</span>
</a>
<a class="tab-item" href="#" data-bind="click: updateYoutube, clickBubble: false, css: { active: isYoutubeVisible }">
<span class="icon"><i class="fa fa-youtube" data-bind="css: { 'fa-youtube': isYoutubeReady | !isYoutubeLoading(), 'fa-spinner': isYoutubeLoading, 'fa-spin': isYoutubeLoading }"></i></span>
<span class="tab-label">YouTube</span>
</a>
<a class="tab-item" href="#" data-bind="click: updateTwitter, clickBubble: false, css: { active: isTwitterVisible }">
<span class="icon"><i class="fa fa-twitter" data-bind="css: { 'fa-twitter': isTwitterReady | !isTwitterLoading(), 'fa-spinner': isTwitterLoading, 'fa-spin': isTwitterLoading }"></i></span>
<span class="tab-label">Twitter</span>
</a>
</nav>
<!-- Includ JS -->
<!-- <script src="http://cdnjs.cloudflare.com/ajax/libs/ratchet/2.0.2/js/ratchet.min.js"></script> -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/zepto/1.1.3/zepto.min.js" type="text/javascript"></script>
<script src="main.js" type="text/javascript"></script>
</body>
</html>
.bar-nav~.content {
padding-bottom: 35px;
}
.content>.table-view:first-child {
margin-top: 0px;
}
.content .videos .video .badge {
position: absolute;
top: 7.5em;
}
.content .videos .video .badge.left {
left: 2.5em;
}
.content .videos .video .badge.right {
right: 2.5em;
}
/* global Zepto, ko*/
(function(exports, $, ko) {
'use strict';
var currentRequest,
crossdomainRequesting = 'http://social-media-rest-api.herokuapp.com',
setObservableValue = function(observable, value) {
observable = observable || ko.observable();
observable(value);
return observable;
},
retrieveData = function(socialmedia) {
/**
* Learn more XMLHttpRequest
* https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
*
* Using XMLHttpRequest
* https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
*/
return function() {
var jsonpCallback = 'cb',
url = crossdomainRequesting + socialmedia.url,
request = $.ajax({
url: url,
dataType: 'jsonp',
jsonpCallback: jsonpCallback,
success: socialmedia.callback,
complete: function() {
socialmedia.loading(false);
currentRequest = undefined;
}
});
if (currentRequest && currentRequest.abort) {
currentRequest.abort();
}
return loadingSocialMedia(socialmedia, currentRequest = request);
};
},
loadingSocialMedia = function(socialmedia, request) {
socialmedia.data.removeAll();
socialmedia.beVisible();
socialmedia.loading(true);
return request;
},
clearAllSocialMedia = function() {
$.each(SocialMedia, function(name, socialmedia) {
socialmedia.loading(false);
socialmedia.isVisible(false);
});
},
beVisible = function() {
clearAllSocialMedia();
this.isVisible(true);
},
setUpSocialMedia = function() {
$.each(SocialMedia, function(name, socialmedia) {
socialmedia.data = ko.observableArray();
socialmedia.isVisible = ko.observable(false);
socialmedia.loading = ko.observable(false);
socialmedia.gonnaBeReady = ko.computed(function() {
return socialmedia.isVisible() || socialmedia.loading();
});
socialmedia.ready = ko.computed(function() {
return socialmedia.isVisible() && !socialmedia.loading();
});
socialmedia.beVisible = beVisible;
socialmedia.retrieve = retrieveData(socialmedia);
});
},
SocialMedia = {
Facebook: {
url: '/facebook/wall/igor.r.lima.75/atleast/5',
},
Instagram: {
url: '/instagram/photos/250455645',
},
Youtube: {
url: '/youtube/playlists/igorribeirolima',
},
Twitter: {
url: '/twitter/timeline/igorribeirolima',
}
};
setUpSocialMedia();
SocialMedia.Youtube.changeCurrentVideo = function(playlist, currentIndex) {
var previous, lastPosition, next;
currentIndex = currentIndex < 0 ? 0 : currentIndex;
currentIndex = currentIndex >= playlist.videos.length ?
playlist.videos.length - 1 :
currentIndex;
previous = currentIndex;
lastPosition = playlist.videos.length - 1;
next = (currentIndex > lastPosition) ?
lastPosition :
(lastPosition - currentIndex);
playlist.current = setObservableValue(playlist.current, currentIndex);
playlist.previous = setObservableValue(playlist.previous, previous);
playlist.next = setObservableValue(playlist.next, next);
$.each(playlist.videos, function(index, video) {
var isVisible = currentIndex === index;
video.visible = video.visible ? video.visible : ko.observable(isVisible);
video.visible(isVisible);
});
};
SocialMedia.Youtube.callback = function(playlists) {
var changeCurrentVideo = SocialMedia.Youtube.changeCurrentVideo;
$.each(playlists, function(index, playlist) {
changeCurrentVideo(playlist, 0);
playlist.goPrevious = function() {
changeCurrentVideo(playlist, playlist.current() - 1 );
};
playlist.goNext = function() {
changeCurrentVideo(playlist, playlist.current() + 1 );
};
SocialMedia.Youtube.data.push(playlist);
});
};
SocialMedia.Twitter.callback = function(tweets) {
$.each(tweets, function(index, tweet) {
SocialMedia.Twitter.data.push(tweet);
});
};
SocialMedia.Facebook.setUpFeed = function(feed) {
feed.story = feed.story || '';
feed.message = feed.message || '';
feed.name = feed.name || '';
feed.description = feed.description || '';
feed.link = feed.link || '';
feed.text = feed.text || '';
feed.picture = feed.picture || '';
SocialMedia.Facebook.setFeedType(feed);
};
SocialMedia.Facebook.setFeedType = function(feed) {
feed.isLink = setObservableValue(feed.isLink, feed.type === 'link');
feed.isStatus = setObservableValue(feed.isStatus, feed.type === 'status');
feed.isPhoto = setObservableValue(feed.isPhoto, feed.type === 'photo');
feed.isQuestion = setObservableValue(feed.isQuestion, feed.type === 'question');
};
SocialMedia.Facebook.callback = function(data) {
$.each(data.feeds, function(index, feed) {
SocialMedia.Facebook.setUpFeed(feed);
SocialMedia.Facebook.data.push(feed);
});
};
SocialMedia.Instagram.callback = function(photos) {
$.each(photos, function(index, photo) {
SocialMedia.Instagram.data.push(photo);
});
};
ko.bindingHandlers.hidden = {
update: function(element, valueAccessor) {
ko.bindingHandlers.visible.update(element, function() {
return !ko.utils.unwrapObservable(valueAccessor());
});
}
};
var bindings = {};
$.each(SocialMedia, function(name, socialmedia){
bindings['dataFrom'+name] = socialmedia.data;
bindings['is'+name+'Visible'] = socialmedia.isVisible;
bindings['update'+name] = socialmedia.retrieve;
bindings['is'+name+'Loading'] = socialmedia.loading;
bindings['is'+name+'Ready'] = socialmedia.ready;
bindings['is'+name+'GonnaBeReady'] = socialmedia.gonnaBeReady;
});
$('.social-media').each(function(index, item) {
ko.applyBindingsToDescendants(bindings, item);
});
SocialMedia.Youtube.retrieve();
console.warn(true);
exports.SocialMedia = SocialMedia;
}(window, Zepto, ko));
{
"name": "minhas-midias-sociais",
"version": "0.0.1",
"dependencies": {
"express": "~3.4.7"
},
"engines": {
"node": ">=0.10.25"
}
}
web: node server.js
var express = require('express');
var app = express()
.use(express.static(__dirname + '/'))
.listen(process.env.PORT || 5000);
@ghost

This comment has been minimized.

Copy link

commented Jun 23, 2014

showw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.