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

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 Search and displaying the News Articles. I am going to keep it simple and straight to the point. No bootstrap and we will have 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 basic understanding of Node.js and Express. For more in information please watch the video.

Please note that my hosting is fairly slow and if too many of us use it for this tutorial it could potentially crash. I advice you to use one of the listed API’s above or use your own WordPress website. Alternatively you can try using mine. Links below:

WP Endpoints

https://raddy.co.uk/wp-json/wp/v2/posts/
https://raddy.co.uk/wp-json/wp/v2/posts?search=photoshop
https://raddy.co.uk/wp-json/wp/v2/posts/5372
https://raddy.co.uk/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
πŸ“‚ routes
 πŸ“œ index.js
πŸ“‚ views
 🌍 index.js
πŸ“œ 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
[x] Dotenv
[x] EJS
[x] Express
[x] Axios

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

npm install ejs express body-parser 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 out 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 in 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'))
app.use('/css', express.static(__dirname + 'public/css'))
app.use('/img', express.static(__dirname + 'public/img'))
app.use('/js', express.static(__dirname + 'public/js'))

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

app.use(bodyParser.urlencoded({ extended : true }))

// 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> 
<!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> 
<!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-single">
                <a href="/">←- Back</a>
                <% if(article != null) { %>
                    <h2><%- article.title.rendered %></h2>
                    <p><%- article.content.rendered %></p>
                <% } 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.co.uk/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.co.uk/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.co.uk/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.

Download

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

Leave a Reply

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