Skip to content

Instantly share code, notes, and snippets.

@igorlima
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

@ghost ghost commented Jun 23, 2014

showw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment