Modern applications communicate with other servers to accomplish tasks like sending emails, upload/download images, and embedding live Twitter feed. That is when we need HTTP requests. There are multiple ways to make HTTP requests in Node.js. In this article, I am going to introduce the Axios library.
Axios is a JavaScript library that works in both Browser and Node.js platforms. Axios is promise-based, and this lets us perform requests asynchronously.
Implementation
Let's implement a feature in the Node.js-React application I created in the last article that accepts a URL as an input from a user, load the content of the URL, and counts the number of word occurrence in its DOM. I am going to use a URL of a large .txt
document of The Mysterious Affair at Styles by my favorite mystery writer, Agatha.
Before we move on, let's create api/app.js
and edit api/server.js
to separate responsibilities:
// api/app.js
const express = require("express")
const app = express()
const cors = require("cors")
app.use(cors())
app.post("/", function (req, res) {
// fetch the content from the URL
// clean the content
// count the word occurrence and send it back
})
module.exports = app
// api/server.js
const app = require("./app")
app.listen(3000, () => {
console.log("app listening on port 3000")
})
Now, let's create a module for each task:
- Fetch the content from the URL
- Clean the content
- Count the word occurrence ***
Fetch the content from the URL
First, we need to install axios
. Run:
$ cd api
$ npm install axios
Create api/fetch-url-content.js
and write:
// api/fetch-url-content
const axios = require('axios')
exports.fetchUrlContent = url => {
return axios.get(url)
.then(response => {
return response.data
})
.catch(error => {
console.log(error)
})
}
response.data
is the response that was provided by the server. Let's use catch
for error handling.
Clean the content
To count the occurrence properly, we should:
- Remove numbers
- Remove special characters except for apostrophes that are part of the words
- Replace 2 or more spaces to 1
- Remove whitespace from both ends of a string
- Convert strings to lowercase
To do this, we need Regular Expression. Create api/clean.js
and write:
// api/clean.js
exports.clean = string => {
const alphabet = string.replace(/[^A-Za-z']+/g, " ").trim()
const lowerCase = alphabet.toLowerCase()
return lowerCase
}
As the replace() method searches a string for a specified value and returns a new string where the specified values are replaced, .replace(/[^A-Za-z']+/g, " ")
replaces everything besides alphabets, and apostrophes that are neither ends of a string with one space.
The trim()
method removes whitespace from both ends of a string.
The toLowerCase() method returns the calling string value converted to lower case.
Count the word occurrence
Let's create api/count.js
and implement a function to count the word occurrence of cleaned string:
// api/count.js
exports.count = string => {
let map = {}
const words = string.split(" ").filter(word => word !== "")
for (let i = 0; i < words.length; i++) {
const item = words[i]
map[item] = (map[item] + 1) || 1
}
return map
}
Call the functions asyncronously
Now in api/app.js
, we can call functions provided by each module:
// api/app.js
app.post("/", async function (req, res) {
const url = req.body.url
const content = await fetchUrlContent(url)
const cleanedContent = clean(content)
const result = count(cleanedContent)
res.send(result)
})
We need async
and await
to wait for the fetchUrlContent
function to finish executing and then run the rest of the POST
method.
Use body-parser to simplify the POST
request
Also, it is better to use the body-parser Express middleware to read the body of incoming request data, and simplify it. This time we use express.json()
and express.urlencoded
.
Now, api/app.js
should look like this:
// api/app.js
const express = require("express")
const app = express()
const cors = require("cors")
const { fetchUrlContent } = require("./fetch-url-content")
const { clean } = require("./clean")
const { count } = require("./count")
app.use(cors())
app.use(express.urlencoded(({ extended: true })))
app.post("/", async function (req, res) {
const url = req.body.url
const content = await fetchUrlContent(url)
const cleanedContent = clean(content)
const result = count(cleanedContent)
res.send(result)
})
module.exports = app
Build the client
Lastly, let's build a form and a table in client/App.js
for UI:
// client/App.js
import React from "react"
import "./App.css" // Added some styling
class App extends React.Component {
state = {
url: "",
result: {}
}
genRows = obj => {
return Object.keys(obj).map(key => {
return (
<tr key={key}>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
)
})
}
submitHandler = e => {
e.preventDefault()
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accepts: "application/json",
},
body: JSON.stringify({
url: this.state.url,
}),
}
fetch("http://localhost:3000/", options)
.then(res => res.json())
.then(data => {
this.setState({ result: data, url: "" })
})
}
render() {
return (
<>
<h1>Word Counter</h1>
<form onSubmit={e => this.submitHandler(e)}>
<label>
URL:
<input
type="url"
name="url"
onChange={e => this.setState({ url: e.target.value })}
value={this.state.url} />
</label>
<input type="submit" value="Submit" />
</form>
<table>
<thead>
<tr>
<th>Word</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{this.genRows(this.state.result)}
</tbody>
</table>
</>
)
}
}
export default App
This is it! Let's see what we get from The Mysterious Affair at Styles:
Top comments (2)
Hey did u get this output from backend
{
"object": 1,
"promise": 1
}
when i tried to pass the url from the frontend and i for an error from the backend that is,
TypeError [ERR_INVALID_URL]: Invalid URL
but i directly pass the url from backend , i got the above object as output
This is the frontend code
could u please check
`import './App.css';
import { useState } from 'react';
function App() {
const [url, setUrl] = useState("");
const [result, setResult] = useState({});
const genRows=obj=>{
return Object.keys(obj).map(key=>{
return(
)
})
}
const submitHandler=e=>{
e.preventDefault()
const options={
method:'POST',
headers:{
"content-type":"application/json",
Accepts:"application/json",
},
body:JSON.stringify({url:url}),
}
fetch('localhost:5000/',options)
.then(res=>res.json()).then(data=>{
setResult({result:data})
// setUrl('')
})
}
return (
WORD COUNTER
submitHandler(e)}>
type="url"
name="url"
placeholder='Enter The URL'
onChange={e=>setUrl(e.target.value)}
value={url}
/>
{genRows(result)}
);
}
export default App;
`
Thanks for the tutorial!
Quick note - I had to add the following line in server.js to make it work:
app.use(express.json())