Unable to query restful api to output json object in nodejs - javascript

I am building a restful API to fetch data from an external API (https://api.exchangeratesapi.io/latest). The fetch is successful. However, I want a situation where if you query like so "/api/rates?currency=EUR,GBP,USD", you will get
"rates": {
"EUR": 0.0377244605,
"GBP": 0.033795458,
"USD": 0.044824204
}
And if there is no query parameter, all the rates will be returned.
I tried using ternary operator to pull out the figures when a query parameter is present, but I get the error "TypeError: Cannot read property 'rates' of undefined"
Below is my app.js file
const express = require("express");
const request = require('request');
const apiCallFromRequest = require('./Requests')
const app = express();
const exchangeRouter = express.Router();
const port = process.env.PORT || 4001;
exchangeRouter.route(`/rates`)
.get((req, res) => {
const queryParameter = req.query;
// console.log(queryParameter.currency)
apiCallFromRequest.callApi(function(response) {
let cur = queryParameter.currency
res.json({
results: {
base: queryParameter.base ? queryParameter.base : response.base,
date: response.date,
rates: queryParameter.currency ? queryParameter.currency.response.rates : response.rates
}
})
res.end();
});
});
app.use('/api', exchangeRouter);
app.get('/', (req, res) => {
res.send('Welcome to my nodemon API')
});
app.listen(port, () => {
console.log(`Running on port ${port}`);
})
And my Request.js file
const request = require('request');
const URL = `https://api.exchangeratesapi.io/latest`;
const callExternalApiUsingRequest = (callback) => {
request(URL, { json: true }, (err, res, body) => {
if (err) {
return callback(err);
}
// console.log("body: ", body)
return callback({
base: body.base,
date: body.date,
rates: body.rates
});
});
}
module.exports.callApi = callExternalApiUsingRequest;

As I can see you're trying to filter the response. It would be better to inject your query params into your external api call.
You're expecting currency as a query parameter /api/rates?currency=EUR,GBP,USD on your api. There is no response under currency that's why queryParameter.currency.rates is undefined
According to https://exchangeratesapi.io/ you can use symbols to achieve the same result:
GET https://api.exchangeratesapi.io/latest?symbols=USD,GBP HTTP/1.1
you may use the same query params as the external api for yours and use a function to inject them into the external call

The solution. My app.js file should look like this:
const express = require("express");
const request = require('request');
const apiCallFromRequest = require('./Requests')
const app = express();
const exchangeRouter = express.Router();
const port = process.env.PORT || 4001;
exchangeRouter.route(`/rates`)
.get((req, res) => {
apiCallFromRequest.callApi(function(response) {
if (!base && !currency) {
return res.status(200).json({
results: {
"base": response.base,
"date": response.date,
"rates": response.rates
}
})
}
// If there is a query parameter but no value
if (!base) {
return res.status(400).json({ message: "Please specify your home currency" })
}
if (!currency) {
return res.status(400).json({ message: "Please specify exchange currency or currencies" })
}
// If there is query parameter and a value
const exchangeRates = currency.toUpperCase().split(',')
let currencyExchangeRates = {};
for (const exchangeRate of exchangeRates) {
//trim white space
const value = exchangeRate.trim();
currencyExchangeRates = { ...currencyExchangeRates, [value]: response.rates[value] }
}
console.log(currencyExchangeRates);
res.json({
results: {
"base": base,
"date": response.date,
"rates": currencyExchangeRates
}
})
res.end();
});
});
app.use('/api', exchangeRouter);
app.get('/', (req, res) => {
res.send('Welcome to my nodemon API')
});
app.listen(port, () => {
console.log(`Running on port ${port}`);
})

Related

Cannot POST /api/sentiment

I'm testing the endpoint for /api/sentiment in postman and I'm not sure why I am getting the cannot POST error. I believe I'm passing the correct routes and the server is listening on port 8080. All the other endpoints run with no issue so I'm unsure what is causing the error here.
server.js file
const express = require("express");
const cors = require("cors");
const dbConfig = require("./app/config/db.config");
const app = express();
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(express.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));
const db = require("./app/models");
const Role = db.role;
db.mongoose
.connect(`mongodb+srv://tami00:MEUxClWqUNbLz359#cluster0.gmvao.mongodb.net/test?retryWrites=true&w=majority`, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => {
console.log("Successfully connect to MongoDB.");
initial();
})
.catch(err => {
console.error("Connection error", err);
process.exit();
});
// simple route
app.use('/api/favourite', require('./app/routes/favourite.routes'));
app.use('/api/review', require('./app/routes/review.routes'));
app.use('/api/sentiment', require('./app/routes/sentiment-analysis.routes'));
// routes
// require(".app/routes/favourite.routes")(app);
require("./app/routes/auth.routes")(app);
require("./app/routes/user.routes")(app);
// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
function initial() {
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
new Role({
name: "creator"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'creator' to roles collection");
});
new Role({
name: "watcher"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'watcher' to roles collection");
});
}
});
}
sentiment-analysis routes file
const express = require('express');
const router = express.Router();
const getSentiment = require('../sentiment-analysis/sentimentAnalysis')
router.post('/api/sentiment', (req, res) => {
const data = req.body.data
const sentiment = getSentiment(data)
return res.send({sentiment})
})
module.exports = router;
sentimentAnalysis.js file
const aposToLexForm = require("apos-to-lex-form");
const {WordTokenizer, SentimentAnalyzer, PorterStemmer} = require("natural");
const SpellCorrector = require("spelling-corrector");
const stopword = require("stopword");
const tokenizer = new WordTokenizer();
const spellCorrector = new SpellCorrector();
spellCorrector.loadDictionary();
const analyzer = new SentimentAnalyzer('English', PorterStemmer, 'afinn')
function getSentiment(text){
if(!text.trim()) {
return 0;
}
const lexed = aposToLexForm(text).toLowerCase().replace(/[^a-zA-Z\s]+/g, "");
const tokenized = tokenizer.tokenize(lexed)
const correctSpelling = tokenized.map((word) => spellCorrector.correct(word))
const stopWordsRemoved = stopword.removeStopwords(correctSpelling)
console.log(stopWordsRemoved)
const analyzed = analyzer.getSentiment(stopWordsRemoved);
console.log(analyzed)
}
module.exports = getSentiment;
console.log(getSentiment("Wow this is fantaztic!"))
console.log(getSentiment("let's go together?"))
console.log(getSentiment("this is so bad, I hate it, it sucks!"))
I see that you use your routes like: app.use('/api/sentiment', require('./app/routes/sentiment-analysis.routes'));. But then in your sentiment-analysis you again use /api/sentiment so your request URL should be /api/sentiment/api/sentiment
Shouldn't it be:
const data = req.body.data

how to split json into stages. Express API

So the problem takes place in architecture of my API, that I want to place into the array, called items[].
As we can see, here is a simple API wrote on express + mongodb.
But the question is how to place my data from post method into the global array called items[].
It should looks like:
So the code is:
const express = require('express')
const MongoClient = require('mongodb').MongoClient
const ObjectId = require('mongodb').ObjectId
const app = express();
let db;
const port = process.env.PORT || 3015
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.send('Hey')
})
app.get('/artists', (req, res) => {
let count = 10
let page = 0
req.query.count ? count = req.query.count : null
req.query.page ? (page = req.query.page * count, count = page + 5) : null;
db.collection('artists').find().toArray((err, docs) => {
if (err) {
console.log(err)
return res.sendStatus(500)
}
res.send(docs.slice(page, count))
})
}),
app.post('/artists', (req, res) => {
const artist = {
name: req.body.name,
photos: {
small: null,
large: null
},
status: null,
followed: false
}
db.collection('artists').insertOne(artist, (err, result) => {
if (err) {
console.log(err)
return res.sendStatus(500)
}
res.send(artist)
})
})
MongoClient.connect('***********', (err, client) => {
if (err) {
return console.log(err)
}
db = client.db('mongod');
app.listen(port, () => {
console.log(`API is listening on port ${port}...`)
})
})
You can do this easily with res.json() which sends the response as json data.
Change
res.send(docs.slice(page, count))
to
res.json({ items: docs.slice(page, count), status: "success" })

How to increment property in MongoDB collection using $inc

I have a list of articles that have a property views and I want to increment that property in the database each time a user clicks on an article title. Currently nothing happens when I do it. Why isn't it working and how can I increment that property each time on click? Here is my React part:
const incrementViews = (id) => {
var item = posts.find(x => x._id === id);
item.views += 1;
}
<div className="post-title">
<Link to={`/post/${post._id}`}>
<h2><a href="#" onClick={() => incrementViews(post._id)}>{post.title}</a>
</h2>
</Link>
</div>
and my server.js:
// Requiring the dependencies
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
require('dotenv').config();
const mongoose = require('mongoose');
const PORT = process.env.PORT || 3001;
const BASE_URL = process.env.REACT_APP_BASE_URL;
console.log(BASE_URL)
const itemRoutes = express.Router();
let Comment = require('./comment.model');
app.use(cors());
app.use(bodyParser.json());
mongoose.connect(BASE_URL, { useNewUrlParser: true })
const connection = mongoose.connection;
connection.once('open', function () {
console.log('Connection to MongoDB established succesfully!');
});
let collection = connection.collection("posts_with_tags_test");
collection.createIndex(
{
postContent: 'text',
title: 'text'
}
);
// Serve static assets
if (process.env.NODE_ENV === 'production') {
app.use(express.static('build'));
}
itemRoutes.route('/').get(async (req, res) => {
let collection = connection.collection("posts_with_tags_test");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/search').post(async (req, res) => {
let result = await connection.collection("posts_with_tags_test").find({
$text: {
$search: req.body.searchString
}
}).toArray();
res.send(result);
});
itemRoutes.route("increment/:id"").post(async (req, res) => {
const { id } = req.params;
collection.updateOne({ _id: id }, { $inc: { views: 1 } });
return res.status(200).json({ msg: "OK" });
});
itemRoutes.route('/comments').get(async (req, res) => {
let collection = connection.collection("comments");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/comments')
.post((req, res) => {
res.setHeader('Content-Type', 'application/json');
let comment = new Comment(req.body);
comment.save()
.then(comment => {
res.status(200).json({ comment })
})
.catch(err => {
res.status(400).send('failed')
})
});
app.use('/', itemRoutes);
app.use('/comments', itemRoutes);
app.use('/search', itemRoutes);
app.use('/increment', itemRoutes);
app.listen(PORT, function () {
console.log('Server is running on' + ' ' + PORT);
})
I think there are two problems in frontend and backend respectively.
Front-end
You should use post variable as a state variable so as to re-render then component when changes are made on post.
Back-end
There is no issue with increasing view in your code.
Here, you need to return success status.
The function incrementViews only increments views on the frontend and never sends any data to the API. One way you can make it work is as follows:
server.js
itemRoutes.route("/increment/:id").post(async (req, res) => {
const { id } = req.params;
collection.updateOne({ _id: id }, { $inc: { views: 1 } });
return res.status(200).json({ msg: "OK" });
});
React
const incrementViews = (id) => {
// Assuming your API server is running on port 5000.
fetch(`http://localhost:5000/increment/${id}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((res) => res.json())
.then(console.log)
.catch(console.error);
};
Update
The reason you're getting 404 is a missing colon : in the route parameters.
// Notice the :id, colon is important.
itemRoutes.route("/increment/:id").post(async (req, res) => {
const { id } = req.params;
// ...
});
Here is a demo reproduced on Glitch. Removed database logic and just added a response messages.
I tested the demo using Postman and it works fine.
On a POST request to https://adaptive-sassy-legal.glitch.me/increment/123, should return a response as shown below.
{
msg: "itemsRoute increment.",
id: "123"
}
Update 2
Another thing which I forgot to mention in the previous update was to update the middleware.
// Use only `/increment` instead of `increment/:id`.
app.use("/increment", itemRoutes);
Here is an updated demo.

"Sending request" loading on Get request on Postman

My /chat route works well through Post method with validation with Joi schema but when I send request through Get method, it show Sending Request and continue loading...
My index.js file:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const chat = require('./db/ChatModel');
const app = express();
app.use(bodyParser.json());
app.get('/chat', (req, res) => {
chat.getAllMessages().then( (messages) => {
res.json(messages);
});
});
app.post('/chat', (req, res) => {
console.log(req.dody);
chat.createMessages(req.body).then((message) => {
res.json(message);
}).catch( (error) => {
res.status(500);
res.json(error);
});
});
const port = process.env.PORT || 8888;
app.listen(port, () => {
console.log(`Listening on port ${port}...`);
});
In connection.js I coded this
const monk = require('monk');
const connectionString = 'localhost/chatboard';
const db = monk(connectionString);
module.exports = db;
And ChatModal.js has the following code
const Joi = require('joi');
const db = require('./connection');
const schema = Joi.object().keys({
username: Joi.string().alphanum().min(4).max(16).required(),
subject: Joi.string().required(),
message:Joi.string().max(300).required(),
imgUrl: Joi.string().uri({
scheme: [ // https://github.com/hapijs/joi/blob/v14.3.1/API.md#stringurioptions
/https?/
]
})
});
const chat = db.get('chat');
function getAllMessages() {
return chat.find();
};
function createMessages(message) {
const result = Joi.validate(message, schema);
if (result.error == null) {
message.created = new Date();
return chat.insert(message);
} else {
return Promise.reject(result.error);
}
}
module.exports = {
createMessages,
getAllMessages
};
I can't understand why getAllMessages() doesn't work and postman continue loading when Get request applied like this http://prntscr.com/s0d9c5
ChatModal.js
function getAllMessages() {
try {
return chat.find();
} catch (err) {
return next(err);
}
index.js
app.get('/chat', (req, res, next) => {
try{
data = chat.getAllMessages()
} catch (err) {
return next(error);
}
res.json(data);
});
User try-catch in the ChatModal.js and also index.js then you can understand what is actual error, like bellow:
ChatModal.js
function getAllMessages() {
try {
chat.find();
} catch (err) {
return next(err);
}
I think, may be your data, i mean message list data so weight, in this case you get all message,res.json(messages); json method have long time to parse messages data

Use Express JS to block unwanted requests from the Client SIde

Consider the Express router :
const express = require("express");
const router = express.Router();
const DUMMY_PLACES = [
{
id: "p1",
title: "Empire State Building",
description: "One of the most famous sky scrapers in the world!",
location: {
lat: 40.7484474,
lng: -73.9871516
},
address: "20 W 34th St, New York, NY 10001",
creator: "u1"
}
];
// # http://localhost:5000/api/places/user/u1
router.get("/user/:uid", (req, res, next) => {
const user_id = req.params.uid;
const place = DUMMY_PLACES.find(p => {
return p.creator === user_id;
});
return res.status(200).json({
place
});
});
module.exports = router;
And the Server :
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const placesRoutes = require("./routes/places-routes");
app.use("/api/places", placesRoutes);
const PORT = 5000;
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
When clients hit the request http://localhost:5000/api/places/user/u1 , they get the dummy object ... however when hitting the request
http://localhost:5000/api/places/user
... it produces an empty object.
How can I return something like NOT ALLOWED instead of the empty object ?
Maybe you could check for the existence of a user_id and send an error response if there isn't one?
router.get('/user/:uid', (req, res, next) => {
const user_id = req.params.uid
if (!user_id) {
return res.status(400).json({
error: 'User ID required'
})
}
const place = DUMMY_PLACES.find((p) => {
return p.creator === user_id
})
return res.status(200).json({
place
})
})
The HTTP status codes are born to handle a lot of situation. In your case, there is a client error: the resource requested has not been found on the server (error 404).
In this case, your API can change in this way:
router.get("/user/:uid", (req, res, next) => {
const user_id = req.params.uid;
const place = DUMMY_PLACES.find(p => {
return p.creator === user_id;
});
if (!place) { // if the place does not exist
return res.status(404).json({
message: 'The requested resource has not been found in the server.'
});
}
return res.status(200).json({
place
});
});

Categories