Webiny Blog
Kevine Nzapdi

Build a News App With Webiny, Next.js, and News API (2/2)

In the first part of this tutorial, we learned how to create a Webiny project, build our model then add content and finally fetch and display in Next.js frontend. In this second part, we will get and store the data from a NewsAPI into Webiny and finally deploy our news application on Netlify. Below is a preview of what we will be building in this tutorial.

Prerequisites

Get and Post News Data in Webiny Using Axios and NewsAPI

Create NewsAPI Account

Create a NewsAPI account and generate your API Key. Once you have your API Key, add it to your env.local file:

NEXT_PUBLIC_NEWS_API=Your News API key
NEXT_PUBLIC_NEWS_API_URL=Your News API Url

Install Axios

Install axios with the code below

yarn add axios

Create Axios Client

Create src/lib/axiosAPI.js file, import axios and add the following code:

import axios from "axios";

const axiosClient = axios.create({
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.NEXT_PUBLIC_WEBINY_API_SECRET}`,
  },
});

export default axiosClient;

In the header we have the Webiny authorization since we will be making post requests to add data in Webiny and the content type is a JSON application

Now we will create two queries:

Publish News Post

const publish = ` 
mutation publishNew($id:ID!) {
  publishNew(revision:$id) {
    data{
      id
      title
      excerpt
      description
      author
    }
  }
} 
`;

When you create a post in Webiny, it only saves it in the database. To publish it we need to write a POST request and pass the id of the post to be published in the query

Create News Post

const create = `
mutation createNew($title:String!, $excerpt:String!, $author:String!, $description:String!, $slugUrl:String!, $image:String!) {
  createNew(data:{title:$title, excerpt:$excerpt, author:$author, description:$description, slugUrl:$slugUrl, image:$image}) {
    data {
      id
      title
      excerpt
      description
      author
      image
    }
  }
}
`;

This is the mutation for adding a news post, it takes as an argument the various attributes of a news post: Title, Excerpt, Author, Description, Image and SlugUrl

Now that we have our queries let’s write two functions to create and publish a news post.

export const url = `${process.env.NEXT_PUBLIC_NEWS_API_URL}domains=${domain}&pageSize=${pageSize}&from=2024-01-13&to=2024-01-16&apiKey=${process.env.NEXT_PUBLIC_NEWS_API}`;
const cleanSlug = (urls) => {
  urls = urls.replace(/\/$/, "");
  return urls.split("/").pop();
};

export async function addNews(data) {
  await axiosClient({
    url: process.env.NEXT_PUBLIC_WEBINY_API_MANAGE_URL,
    method: "POST",
    data: JSON.stringify({
      query: create,
      variables: {
        title: data.title,
        excerpt: data.description,
        author: data.author,
        description: data.content,
        slugUrl: cleanSlug(data.url),
        image: data.urlToImage,
      },
    }),
  })
    .then((res) => {
       publishNews(res.data);
    })
    .catch((err) => {
      console.log("something happened", err);
    });
}

In the code above, we create a post request where we pass our Webiny manage API, the create query and our query variables. We get the NewsAPI variables and match the variable of our content model.

This is a sample response of NewsAPI:

articles": [

    -
    {
        -
        "source": {
            "id": "engadget",
            "name": "Engadget"
        },
        "author": "Karissa Bell",
        "title": "SEC approves bitcoin ETFs (for real this time)",
        "description": "The Securities and Exchange Commission has approved\r\n the applications of 11 spot bitcoin ETFs in a highly anticipated decision that will make it much easier for people to dabble in cryptocurrency investing without directly buying and holding bitcoin. The app…",
        "url": "https://www.engadget.com/sec-approves-bitcoin-etfs-for-real-this-time-224125584.html",
        "urlToImage": "https://s.yimg.com/ny/api/res/1.2/n6iLNJ_9dtK.fT6WAXK1sA--/YXBwaWQ9aGlnaGxhbmRlcjt3PTEyMDA7aD03OTU-/https://s.yimg.com/os/creatr-uploaded-images/2024-01/3edf5140-afdd-11ee-bf7c-7918e1b9d963",
        "publishedAt": "2024-01-10T22:41:25Z",
        "content": "The Securities and Exchange Commission has approved\r\n the applications of 11 spot bitcoin ETFs in a highly anticipated decision that will make it much easier for people to dabble in cryptocurrency in… [+1453 chars]"
    },
]

Notice that the description of our new model corresponds to the content of the NewsAPI, the image matches the urlToImage, the excerpt corresponds to the description , and the slugUrl matches the url

Since the NewsAPI URL comes in the form [https://tech.com/2024/12/01/my-slug-text](https://tech.com/2024/12/01/my-slug-text) since we don’t want to store it in the Webiny like that, we will apply the cleanSlug function which will only keep the part after the /

The first portion of the addNews() simply creates a draft new post in Webiny.

Draft Post

To publish the post we need the publishNews() and if any error occurs in the process, we catch it and log it in the console

export async function publishNews(data) {
  await axiosClient({
    url: process.env.NEXT_PUBLIC_WEBINY_API_MANAGE_URL,
    method: "POST",
    data: JSON.stringify({
      query: publish,
      variables: {
        id: data?.data?.createNew?.data?.id,
      },
    }),
  })
    .then((res) => {
      console.log(res.data);
    })
    .catch((err) => {
      console.log("Something happened", err);
    });
}

The publishNews() is similar to the addNews() excerpt that the publishNews() takes in the publish query and we only need the id of the post to be published.

Published Entry

This is the complete body of the axiosAPI.js file

import axios from "axios";

let domain = "techcrunch.com,thenextweb.com";
let pageSize = 10;
export const url = `${process.env.NEXT_PUBLIC_NEWS_API_URL}domains=${domain}&pageSize=${pageSize}&from=2024-01-13&to=2024-01-16&apiKey=${process.env.NEXT_PUBLIC_NEWS_API}`;
const cleanSlug = (urls) => {
  urls = urls.replace(/\/$/, "");
  return urls.split("/").pop();
};

// create axios client
const axiosClient = axios.create({
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.NEXT_PUBLIC_WEBINY_API_SECRET}`,
  },
});

export default axiosClient;

// list of queries
const create = `
mutation createNew($title:String!, $excerpt:String!, $author:String!, $description:String!, $slugUrl:String!, $image:String!) {
  createNew(data:{title:$title, excerpt:$excerpt, author:$author, description:$description, slugUrl:$slugUrl, image:$image}) {
    data {
      id
      title
      excerpt
      description
      author
      image
    }
  }
}
`;

const publish = ` 
mutation publishNew($id:ID!) {
  publishNew(revision:$id) {
    data{
      id
      title
      excerpt
      description
      author
    }
  }
} 
`;

// publish news function
export async function publishNews(data) {
  await axiosClient({
    url: process.env.NEXT_PUBLIC_WEBINY_API_MANAGE_URL,
    method: "POST",
    data: JSON.stringify({
      query: publish,
      variables: {
        id: data?.data?.createNew?.data?.id,
      },
    }),
  })
    .then((res) => {
      console.log(res.data);
    })
    .catch((err) => {
      console.log("Something happened", err);
    });
}

// post news function
export async function addNews(data) {
  await axiosClient({
    url: process.env.NEXT_PUBLIC_WEBINY_API_MANAGE_URL,
    method: "POST",
    data: JSON.stringify({
      query: create,
      variables: {
        title: data.title,
        excerpt: data.description,
        author: data.author,
        description: data.content,
        slugUrl: cleanSlug(data.url),
        image: data.urlToImage,
      },
    }),
  })
    .then((res) => {
       publishNews(res.data);
    })
    .catch((err) => {
      console.log("something happened", err);
    });
}

Finally, we will add the following code to our index.js

import {useEffect, useState} from "react";
import axiosClient, {url, addNews} from "@/lib/axiosApi";
const [data, setData] = useState();

  useEffect(() => {
    axiosClient({
      url,
      method: "GET",
    })
      .then((response) => {
        console.clear();
        setData(response.data);
        response.data?.articles?.map((item) => {
           addNews(item);
        });
      })
      .catch((err) => {
        console.log(err);
      });
  }, []);

Here we pass the NewsAPI URL in Axios to get the list of news posts, set the data then map the item in the NewsAPI response to the addNews()

From Here our application should be ready for deployment. We will be deploying on Netlify. So you need to create a Git repository and push your code on that repository because we will be using it for deployment. If you don’t know how to push a code on GitHub you can follow this tutorial

Deploying on Netlify

Create an account on Netlify or Login, go to the Sites menu click on Add new site→Import an Existing project

Import an exisiting project

On the next page, select Deploy with GitHub

Select git repository

NB: You may be prompted to authorize Netlify to access your GitHub repo or Configure the app on GitHub

Select repository

Here you select the newsblog repo, select the team and branch to deploy

Cconfigure Deploy

Add the build command, public directory and finally our environment variables and click on Deploy newsblog

Add var

Wait till the deployment terminates and navigate to the URL to see your application.

This is the News App we built and deployed and you can also check the GitHub repo to view the full code source

Conclusion

In this tutorial, we successfully created and deployed a news application using Axios, Webiny, and Next.js. In the first part of this tutorial, we illustrated the process of setting up a Webiny project and a Next.js application, followed by fetching and displaying news data. In this second part, we delved into retrieving and posting news data from an external API via Axios, culminating in the deployment on Netlify. As a supplementary challenge, consider incorporating a search functionality for news posts or implementing pagination, allowing for the display of 10-20 news posts per page. This article provides insights into adding pagination.

Full source code: https://github.com/webiny/write-with-webiny/tree/main/tutorials/nextjs-news-app


This article was written by a contributor to the Write with Webiny program. Would you like to write a technical article like this and get paid to do so? Check out the Write with Webiny GitHub repo.

One platform for all your content needs!

Webiny is customizable open-source content platform for enterprises. It features a drag&drop page builder, a scalable headless CMS, digital asset manager, publishing workflows and more.

Webiny screenshot

© 2025 Webiny, Inc. All rights reserved.