Reputation: 8550
I had the following index.js file:
const express = require('express');
const path = require('path');
const generatePassword = require('password-generator');
const fetch = require('node-fetch');
const FetchTweets = require('fetch-tweets');
const request = require('request');
const TW_URL = "http://1.1/search/tweets.json" // Twitter search URL
const SEN_URL = "http://www.sentiment140.com/api/bulkClassifyJson" // URL of sentiment analysis
var TW_KEYS = {
consumer_key: process.env.TW_KEY,
consumer_secret: process.env.TW_SECRET
}
const app = express();
const fetchTweets = new FetchTweets(TW_KEYS);
// Serve static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));
// Put all API endpoints under '/api'
app.get('/api/passwords', (req, res) => {
const count = 5;
// Generate some passwords
const passwords = Array.from(Array(count).keys()).map(i =>
generatePassword(12, false)
)
// Return them as json
res.json(passwords);
console.log(`Sent ${count} passwords`);
});
// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
const port = process.env.PORT || 5000;
app.listen(port);
console.log(`Password generator listening on ${port}`);
Along with my react App.js:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
// Initialize state
state = { passwords: [] }
// Fetch passwords after first mount
componentDidMount() {
this.getPasswords();
}
getPasswords = () => {
// Get the passwords and store them in state
fetch('/api/passwords')
.then(res => res.json())
.then(passwords => this.setState({ passwords }));
}
render() {
const { passwords } = this.state;
return (
<div className="App">
{/* Render the passwords if we have them */}
{passwords.length ? (
<div>
<h1>5 Passwords.</h1>
<ul className="passwords">
{/*
Generally it's bad to use "index" as a key.
It's ok for this example because there will always
be the same number of passwords, and they never
change positions in the array.
*/}
{passwords.map((password, index) =>
<li key={index}>
{password}
</li>
)}
</ul>
<button
className="more"
onClick={this.getPasswords}>
Get More
</button>
</div>
) : (
// Render a helpful message otherwise
<div>
<h1>No passwords :(</h1>
<button
className="more"
onClick={this.getPasswords}>
Try Again?
</button>
</div>
)}
</div>
);
}
}
export default App;
It worked beautifully. Now, I want to add a new route so I've changed index.js to:
const express = require('express');
const path = require('path');
const generatePassword = require('password-generator');
const fetch = require('node-fetch');
const FetchTweets = require('fetch-tweets');
const request = require('request');
const TW_URL = "http://1.1/search/tweets.json" // Twitter search URL
const SEN_URL = "http://www.sentiment140.com/api/bulkClassifyJson" // URL of sentiment analysis
var TW_KEYS = {
consumer_key: process.env.TW_KEY,
consumer_secret: process.env.TW_SECRET
}
const app = express();
const fetchTweets = new FetchTweets(TW_KEYS);
// Serve static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('/api/sentiment', async (req, res) => {
console.log("matched /api/sentiment pattern")
const options = {
q : req.query.q,
lang : "en",
count : 100,
}
try{
fetchTweets.byTopic(options, async function(results){
const tweets = {"data": results.map(function(tweet){
return {"text": tweet.body, "query": options.q}
})}
var body = JSON.stringify(tweets)
// get sentiments
const sentiments = await fetch(SEN_URL, {method: "POST", body: body})
const json = await sentiments.json()
const data = json.data
//console.log(data)
// calculate percentages
const response = {positive: undefined, neutral: undefined, negative: undefined}
var numPos = 0
var numNeu = 0
var numNeg = 0
//console.log(response)
data.forEach(function(tweet){
switch(tweet.polarity){
case 4:
numPos += 1
break
case 2:
numNeu += 1
break
case 0:
numNeg += 1
break
}
})
const tot = numPos + numNeu + numNeg
response.positive = numPos/tot
response.neutral = numNeu/tot
response.negative = numNeg/tot
// send response
res.send(response)
})
}catch (error){
console.log(error)
}
})
// Put all API endpoints under '/api'
app.get('/api/passwords', (req, res) => {
const count = 5;
// Generate some passwords
const passwords = Array.from(Array(count).keys()).map(i =>
generatePassword(12, false)
)
// Return them as json
res.json(passwords);
console.log(`Sent ${count} passwords`);
});
// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
const port = process.env.PORT || 5000;
app.listen(port);
console.log(`Password generator listening on ${port}`);
Note the new route /api/sentiments
. I've also made a call to this new URL path in my App.js:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
// Initialize state
state = { passwords: [] }
// Fetch passwords after first mount
componentDidMount() {
this.getPasswords();
this.getSentiments();
}
getSentiments = () => {
fetch("/api/sentiment?q=hello")
.then(res => console.log(res))
}
The getSentiments
function is what does the api call to the new route. But this doesn't work. Here's the browser console message:
GET https://glacial-refuge-37109.herokuapp.com/api/sentiment?q=hello 503 (Service Unavailable)
i.getSentiments @ App.js:15
value @ App.js:11
commitLifeCycles @ react-dom.production.min.js:159
C @ react-dom.production.min.js:185
w @ react-dom.production.min.js:182
g @ react-dom.production.min.js:181
v @ react-dom.production.min.js:181
h @ react-dom.production.min.js:180
s @ react-dom.production.min.js:179
t @ react-dom.production.min.js:190
updateContainer @ react-dom.production.min.js:191
nn.render @ react-dom.production.min.js:228
(anonymous) @ react-dom.production.min.js:242
unbatchedUpdates @ react-dom.production.min.js:188
ln @ react-dom.production.min.js:242
render @ react-dom.production.min.js:244
(anonymous) @ index.js:7
t @ bootstrap 8940ebd453621d06336e:19
(anonymous) @ main.1f99a125.js:526
t @ bootstrap 8940ebd453621d06336e:19
(anonymous) @ bootstrap 8940ebd453621d06336e:62
(anonymous) @ bootstrap 8940ebd453621d06336e:62
App.js:16 Response {type: "basic", url: "https://glacial-refuge-37109.herokuapp.com/api/sentiment?q=hello", redirected: false, status: 503, ok: false, …}
Looks like I'm getting 503, service unavailable. Why? How can I make this work? Btw, this works fine locally.
Upvotes: 0
Views: 44
Reputation: 9241
Add a res.send
within your catch block
app.get('/api/sentiment', async (req, res) => {
console.log("matched /api/sentiment pattern")
const options = {
q : req.query.q,
lang : "en",
count : 100,
}
try{
fetchTweets.byTopic(options, async function(results){
const tweets = {"data": results.map(function(tweet){
return {"text": tweet.body, "query": options.q}
})}
var body = JSON.stringify(tweets)
// get sentiments
const sentiments = await fetch(SEN_URL, {method: "POST", body: body})
const json = await sentiments.json()
const data = json.data
//console.log(data)
// calculate percentages
const response = {positive: undefined, neutral: undefined, negative: undefined}
var numPos = 0
var numNeu = 0
var numNeg = 0
//console.log(response)
data.forEach(function(tweet){
switch(tweet.polarity){
case 4:
numPos += 1
break
case 2:
numNeu += 1
break
case 0:
numNeg += 1
break
}
})
const tot = numPos + numNeu + numNeg
response.positive = numPos/tot
response.neutral = numNeu/tot
response.negative = numNeg/tot
// send response
res.send(response)
})
}catch (error){
console.log(error)
res.send(error)
}
})
Your function is failing and because you don't send a response, hangs forever
Upvotes: 1