Raddy Website Design & Development Tutorials

Build News Website With Node.js, Express & EJS – WP Rest API + newsApi

By Raddy in NodeJs ·

Today we are going to build a simple News website/app using Node.js, Express, EJS and we’ll be also using some dependencies such as AXIOS, Body-Parser and Nodemon.

The website is going to have two main features which are Searching and displaying News Articles. I am going to keep it simple and straight to the point. No bootstrap and we will have a very minimal amount of CSS (SCSS).

The data for the articles will come from my personal website, but feel free to use whatever API you wish.

Here are some good suggestions:

- Your own WordPress site
- Newsapi.org
- Bing News API
- Medium
- Twitter

Before you start, you need to make sure that you have Node.js installed and have a basic understanding of Node.js and Express. For more information please watch the video.

I advise you to use one of the listed API’s above or use your own WordPress website. Alternatively, you can try using my endpoints listed below:

There is a small chance that my firewall might stop you from reaching the endpoints. You can test them by visiting the URL’s below.

WP Endpoints

URL’s have changed from ‘.co.uk’ to ‘.dev’.

https://raddy.dev/wp-json/wp/v2/posts/
https://raddy.dev/wp-json/wp/v2/posts?search=photoshop
https://raddy.dev/wp-json/wp/v2/posts/5372
https://raddy.dev/wp-json/wp/v2/posts?_embed

_embeded gives you more data to work with.

Initialize New Project

To initialise a new Node.js project all you have to do is to create a new project folder “news-app” and then run the Command line or PowerShell in the same directory. Once you do that to initialise a new project simply put the following command:

npm init

This will initialise a new project for you and it’s going to ask you a few questions about your project. The most important one is to give your project a name and then you can just keep pressing enter until the installation is over.

Project Structure

Now let’s create the following folders and files, leaving node_modules, readme.md, package-lock and package-json as that should have been automatically generated by now.

πŸ“‚ node_modules
πŸ“‚ public
 πŸ“‚ css
  πŸ“œ styles.css
  πŸ“œ styles.scss
 πŸ“‚ img
  πŸ–Ό default.jpg
πŸ“‚ src
 πŸ“‚ routes
  πŸ“œ news.js
 πŸ“‚ views
  πŸ“‚ partials
   πŸ“œ search.ejs
  πŸ“œ news.ejs
  πŸ“œ newsSearch.ejs
  πŸ“œ newsSingle.ejs
πŸ“œ README.md
βš“ .env
🌍 app.js
πŸ“œ package-lock.json
πŸ“œ package-json

Dependencies Installation

There are a few dependencies that we need to install to get started. Here is the list:

[x] Body-parser [deprecated]
[x] Dotenv
[x] EJS
[x] Express
[x] Axios

Let’s do that by opening the terminal / powershell and installing the dependencies by typing the following command:

You no longer need to install the body-parser. It comes with Express

npm install ejs express dotenv axios

Restarting the local server

Restarting the server automatically would be annoying. To save us some time let’s quickly install Nodemon.

npm install --save-dev nodemon

To setup up the application to run with nodemon just add the “start” line under scripts in your package.json file.

 "scripts": {
    "start": "nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

Start our local server

To start our application / our local server simply type the following command in the command line:

npm start

Hopefully, everything should be working just fine and you won’t have any errors. Obviously, at this point, we haven’t yet started creating our website. Let’s do that.

Application

Let’s now create our application file. This file will be called app.js and it will sit at the root of our website.

In this file, we need to do a couple of things. We need to require some of the dependencies that we will be working with and we also need to set up our server.

const express = require('express')
const bodyParser = require('body-parser')


const app = express()
const port = 5000

// Static Files
app.use(express.static('public'))

// Templating Engine
app.set('views', './src/views')
app.set('view engine', 'ejs')

// Parsing middleware
// Parse application/x-www-form-urlencoded
// app.use(bodyParser.urlencoded({ extended: false })); // Deprecated
app.use(express.urlencoded({extended: true})); // New



// Routes
const newsRouter = require('./src/routes/news')

app.use('/', newsRouter)
app.use('/article', newsRouter)

// Listen on port 5000
app.listen(port, () => console.log(`Listening on port ${port}`))

Views

Let’s start by building our home page/news page. In the views folder, you should have news.ejs file by now. Let’s create a very simple HTML file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Node.js News</title>
    <link rel="stylesheet" href="/css/styles.css">
    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
</head>
<body>
    <header class="header">
        <div class="header__logo">Node.Js News</div>
        <%- include('./partials/search.ejs') %>
    </header>

    <div class="wrapper">
        <div class="news">
            <% if(articles != null) { %>
            <% articles.forEach(function(article, index) { %>
                <a href="/article/<%- article.id %>" class="news__card">
                    <img src="<%- article.thumbnail_url %>" alt="<%- article.title.rendered %>">
                    <h2><%- article.title.rendered %></h2>
                    <p><%- article.excerpt.rendered %></p>
                </a>
            <% }) %>
            <% } else { %>
                No posts found.
            <% } %>
        </div>
    </div>  
</body>
</html> 

Routes

const express = require('express')
const newsRouter = express.Router()
const axios = require('axios')

newsRouter.get('', async(req, res) => {
    try {
        const newsAPI = await axios.get(`https://raddy.dev/wp-json/wp/v2/posts/`)
        res.render('news', { articles : newsAPI.data })
    } catch (err) {
        if(err.response) {
            res.render('news', { articles : null })
            console.log(err.response.data)
            console.log(err.response.status)
            console.log(err.response.headers)
        } else if(err.requiest) {
            res.render('news', { articles : null })
            console.log(err.requiest)
        } else {
            res.render('news', { articles : null })
            console.error('Error', err.message)
        }
    } 
})

newsRouter.get('/:id', async(req, res) => {
    let articleID = req.params.id

    try {
        const newsAPI = await axios.get(`https://raddy.dev/wp-json/wp/v2/posts/${articleID}`)
        res.render('newsSingle', { article : newsAPI.data })
    } catch (err) {
        if(err.response) {
            res.render('newsSingle', { article : null })
            console.log(err.response.data)
            console.log(err.response.status)
            console.log(err.response.headers)
        } else if(err.requiest) {
            res.render('newsSingle', { article : null })
            console.log(err.requiest)
        } else {
            res.render('newsSingle', { article : null })
            console.error('Error', err.message)
        }
    } 
})


newsRouter.post('', async(req, res) => {
    let search = req.body.search
    try {
        const newsAPI = await axios.get(`https://raddy.dev/wp-json/wp/v2/posts?search=${search}`)
        res.render('newsSearch', { articles : newsAPI.data })
    } catch (err) {
        if(err.response) {
            res.render('newsSearch', { articles : null })
            console.log(err.response.data)
            console.log(err.response.status)
            console.log(err.response.headers)
        } else if(err.requiest) {
            res.render('newsSearch', { articles : null })
            console.log(err.requiest)
        } else {
            res.render('newsSearch', { articles : null })
            console.error('Error', err.message)
        }
    } 
})


module.exports = newsRouter 

CSS

body {
    margin: 0;
    font-family: 'Source Sans Pro', sans-serif;
    background-color: #f6f6f6;
}

img { max-width: 100%; }
h2 { font-size: 1.6rem; }

.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
    color: #fff;
    background-color: #10555A;
    margin-bottom: 10px;

    &__search {
        input[type=text] {
            padding: 6px;
            border: none;
        }

        input[type=submit] {
            float: right;
            padding: 6px 10px;
            border: none;
            cursor: pointer;
        }
    }
}

.wrapper { padding: 0 1rem }

.news {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
    grid-gap: 2rem;

    &__card {
        text-decoration: none;
        color: rgb(8, 8, 8);
        background-color: #fff;
        padding: 20px;

        &:hover {
            box-shadow: 0 3px 3px rgba(0,0,0,0.16), 0 3px 3px rgba(0,0,0,0.23);
        }
        
    }
}

.news-single {
    background-color: #fff;
    max-width: 1300px;
    margin: 0 auto;
    padding: 2rem;
}

API Development tool

Postman is a collaboration platform for API development. Postman’s features simplify each step of building an API and streamline collaboration so you can create better APIsβ€”faster.

Postman

The tool I was using in the video to Get data is Postman.

Examples:

Download

Thank you for reading this article. Please consider subscribing to my YouTube Channel.

Want to deploy your project for free on Heroku? Read the article

YouTube Questions

How to get and display the articles from the NewsApi.org

This example is only for the home (news.ejs) page. It’s more or less the same as the WordPress example, but you just have to change the names. In this example, the link goes straight to the article as the content the API returns is not long enough to be on another page (in my opinion).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Node.js News</title>
    <link rel="stylesheet" href="/css/styles.css">
    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
</head>
<body>
    <header class="header">
        <div class="header__logo">Node.Js News</div>
        <%- include('./partials/search.ejs') %>
    </header>

    <div class="wrapper">
        <div class="news">
            <% if(articles != null) { %>
            <% articles.forEach(function(article, index) { %>
                <a href="<%- article.url %>" class="news__card">
                    <img src="<%- article.urlToImage %>" alt="<%- article.title %>">
                    <h2><%- article.title %></h2>
                    <p><%- article.description %></p>
                </a>
            <% }) %>
            <% } else { %>
                No posts found.
            <% } %>
        </div>
    </div>  
</body>
</html> 

The difference would be that we need to access the data object and then go into the articles. That’s pretty much it. This is only the GET Axios NewsApi example. Make sure that you add your API key.

newsRouter.get('', async(req, res) => {
    try {
        const newsAPI = await axios.get(`http://newsapi.org/v2/everything?q=bitcoin&from=2020-10-30&sortBy=publishedAt&apiKey= YOUR API KEY HERE`)
        res.render('news', { articles : newsAPI.data.articles })
    } catch (err) {
        if(err.response) {
            console.log(err.response.data)
            console.log(err.response.status)
            console.log(err.response.headers)
            res.render('news', { articles : null })
        } else if(err.requiest) {
            res.render('news', { articles : null })
            console.log(err.requiest)
        } else {
            res.render('news', { articles : null })
            console.error('Error', err.message)
        }
    } 
})

How to make the Search work?

First, we need to change the JSON file names so they match the NewsApi.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Node.js News</title>
    <link rel="stylesheet" href="/css/styles.css">
    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet">
</head>
<body>
    <header class="header">
        <div class="header__logo">Node.Js News</div>
        <%- include('./partials/search.ejs') %>
    </header>

    <div class="wrapper">
        <div class="news">
            <% if(articles != null) { %>
            <% articles.forEach(function(article, index) { %>
                <a href="<%- article.url %>" class="news__card">
                    <img src="<%- article.urlToImage %>" alt="<%- article.title %>">
                    <h2><%- article.title %></h2>
                    <p><%- article.description %></p>
                </a>
            <% }) %>
            <% } else { %>
                No posts found.
            <% } %>
        </div>
    </div>  
</body>
</html> 

Then we need to swap the URL and go into the object of the article:

newsRouter.post('', async(req, res) => {
    let search = req.body.search
    try {
        const newsAPI = await axios.get(`http://newsapi.org/v2/everything?q=${search}&apiKey= YOUR API KEY HERE`)
        res.render('newsSearch', { articles : newsAPI.data.articles })
    } catch (err) {
        if(err.response) {
            res.render('newsSearch', { articles : null })
            console.log(err.response.data)
            console.log(err.response.status)
            console.log(err.response.headers)
        } else if(err.requiest) {
            res.render('newsSearch', { articles : null })
            console.log(err.requiest)
        } else {
            res.render('newsSearch', { articles : null })
            console.error('Error', err.message)
        }
    } 
})

That’s it. The search should be working.

Quick note: You might want to put something in place to prevent JS Injection Attacks.

Thank you for reading this article. Please consider subscribing to my YouTube Channel.

More Resources:

  1. hindrik says:

    hindrik ΓΆunpuu
    fΓΆr 0 sekunder sedan
    Hi I tried your file and what you did was excellent, I just trying to learn to express downloaded your source code tried and got following Error write EPROTO 14116:error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:c:\ws\deps\openssl\openssl\ssl\record\rec_layer_s3.c:1546:SSL alert number 80 . Tried this that I find on the net didn’t work: npm config set https-proxy http://proxy.example.com:8080

  2. yola says:

    Hey Raddy! Thanks for the post. But you have changed your url from `raddy.co.uk` to `raddy.dev` and might want to change your urls from WP Endpoints

    1. Raddy says:

      I totally forgot to update the URL’s in the article. I will do that now, thank you for letting me know. Also, I’ve added a Firewall to the website to make it more secure which might result in blocking people from accessing the API’s. I think that I allowed the API’s, but I will double-check that.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.