Twitter CRUD application in React, Php & MySql in 10 Minutes

Total
0
Shares
Twitter CRUD application using react php mysql

All applications have 4 operations – Create, Read, Update and Delete. We call them CRUD. In this guide, we will create a “Twitter Feed” CRUD application using React, Php and Mysql.

Plan the application

This application will perform few tasks –

  1. Creating a new tweet (Create).
  2. Listing all the previous tweets (Read).
  3. Editing any existing tweet (Update).
  4. Deleting a tweet (Delete).

We need a server and database for these operations which we will create in Php and MySql.

We will create 4 php files for CRUD operations. They will act as API endpoints for our React app. Let’s say these 4 files are –

  1. posttweet.php – for adding new tweet.
  2. gettweets.php – for fetching all added tweets.
  3. updatetweet.php – for editing a tweet.
  4. deletetweet.php – for deleting a tweet.

If you are familiar with Post and Get requests then you must know that we send data to server using a variable. Then, server returns the response either in text or binary or JSON. We will use JSON. The request-response scheme will be like this –

Operation Client (POST / GET) API Endpoint Server Response
CREATE POST: {tweet: “Some tweet”} posttweet.php {response: “success”}
or
{response: “error”, result: error message}
READ GET gettweets.php {response: “success”, result: array of tweets}
UPDATE POST: {tweet_id: 56, tweet: “New Text”} updatetweet.php {response: “success”}
or
{response: “error”, result: error message}
DELETE POST: {tweet_id: 56} deletetweet.php {response: “success”}
or
{response: “error”, result: error message}

The JSON of single tweet will be –

{
    id: id of tweet,
    tweet: tweet text,
    time: timestamp of tweet post,
}

Building Twitter Feed UI in React

Lets create a new react project, twitterFeed.

npx create-react-app twitterFeed
cd twitterFeed
npm start

Our react app should connect with server to send and fetch tweet data. We will use Axios for it.

Let’s install Axios –

yarn add axios

We are all set to start. The first thing is to create box for posting tweets. We are breaking our UI in 2 components. First, TweetBox which will contain the code for posting tweets. Second, TweetList which will show the list of tweets.

import React, { useEffect, useState } from "react";
import axios from "axios";

export default function App() {
  const [refreshApp, setRefreshApp] = useState(0);
  const apiUrl = '' // enter url where api is hosted
  return (
    <div style={{ width: "400px", margin: "auto" }}>
      <h2>Twitter Feed</h2>
      <TweetBox />
      <TweetList />
    </div>
  );
}

I have created refreshApp state variable so that I can use it to refresh the app from any part of the code. Enter your api domain address in apiUrl constant.

Code for TweetBox component is –

const TweetBox = () => {
    const [tweet, setTweet] = useState("");

    const postTweet = (e) => {
      e.preventDefault();
      var fd = new FormData();
      fd.append("tweet", tweet);
      axios
        .post(apiUrl + "/posttweet.php", fd)
        .then(function (response) {
          if (response.data.response === "success") {
            setRefreshApp(refreshApp === 1 ? 0 : 1);
          } else alert("Error: " + response.data.result);
        })
        .catch(function (error) {
          console.log(error);
        });
    };

    return (
      <div
        style={{
          padding: "10px",
          background: "#4C9BE5",
          borderRadius: "5px",
          boxSizing: "border-box",
          overflow: "hidden"
        }}
      >
        <form onSubmit={postTweet}>
          <textarea
            style={{ width: "100%" }}
            onChange={(e) => setTweet(e.target.value)}
            value={tweet}
          ></textarea>
          <button
            type={"submit"}
            disabled={tweet.length > 0 ? false : true}
            style={{ float: "right" }}
          >
            Post
          </button>
        </form>
      </div>
    );
  };

As discussed earlier, the endpoint for CREATE is posttweet.php. We are sending tweet text in POST variable tweet. And getting response as –

{response: 'success'} or {response: 'error', result: error message}

The rendered output will be like this –

twitter CRUD application post box

The next component of Twitter crud application is the TweetList. Let’s design that. We will have multiple operations here.

This will be the code for displaying tweet list

const TweetList = () => {
    const [tweets, setTweets] = useState([]);
    useEffect(() => {
      axios
        .get(apiUrl + "/gettweets.php")
        .then(function (response) {
          if (response.data.response === "success") {
            setTweets(response.data.result);
          }
        })
        .catch(function (error) {
          console.log(error);
        });
    });

    const SingleTweet = (props) => {
      const [isEditing, setIsEditing] = useState(0);
      const [newtweet, setNewtweet] = useState(props.info.tweet);
      return (
        <div
          style={{
            padding: "10px",
            marginBottom: "10px",
            background: "#eee",
            overflow: "hidden"
          }}
        >
          {isEditing === 1 ? (
            <div>
              <form onSubmit={(e) => editTweet(e, props.info.id, newtweet)}>
                <textarea
                  style={{ width: "100%" }}
                  onChange={(e) => setNewtweet(e.target.value)}
                  value={newtweet}
                ></textarea>
                <button
                  type={"submit"}
                  disabled={newtweet.length > 0 ? false : true}
                  style={{ float: "right" }}
                >
                  Update
                </button>
              </form>
            </div>
          ) : (
            <div>
              <span style={{ color: "blue", fontWeight: "bold" }}>@User</span>
              &nbsp;
              {props.info.tweet}
              <div>
                <a
                  href="#"
                  onClick={(e) => {
                    e.preventDefault();
                    setIsEditing(1);
                  }}
                >
                  Edit
                </a>
                &nbsp;&nbsp;
                <a href="#" onClick={(e) => deleteTweet(e, props.info.id)}>
                  Delete
                </a>
                <span style={{ float: "right" }}>
                  {new Date(props.info.time * 1000).toLocaleString()}
                </span>
              </div>
            </div>
          )}
        </div>
      );
    };

    return (
      <div style={{ marginTop: "10px" }}>
        {tweets.map((tweet) => (
          <SingleTweet key={tweet.id + "_" + tweet.time} info={tweet} />
        ))}
      </div>
    );
  };

We are getting all the tweets using gettweets.php endpoint. The rendered list will look like this –

tweet list

Let’s create code for editing the tweets

const editTweet = (e, tweet_id, newtweet) => {
      e.preventDefault();
      var fd = new FormData();
      fd.append("tweet", newtweet);
      fd.append("tweet_id", tweet_id);
      axios
        .post(apiUrl + "/updatetweet.php", fd)
        .then(function (response) {
          if (response.data.response === "success") {
            setRefreshApp(refreshApp === 1 ? 0 : 1);
          } else alert("Error: " + response.data.result);
        })
        .catch(function (error) {
          console.log(error);
        });
    };

Code for deleting the tweets

const deleteTweet = (e, tweet_id) => {
      e.preventDefault();
      var fd = new FormData();
      fd.append("tweet_id", tweet_id);
      axios
        .post(apiUrl + "/deletetweet.php", fd)
        .then(function (response) {
          if (response.data.response === "success") {
            setRefreshApp(refreshApp === 1 ? 0 : 1);
          } else alert("Error: " + response.data.result);
        })
        .catch(function (error) {
          console.log(error);
        });
    };

Let’s now compile all the functions and components. Open App.js file and replace its content with this code –

import React, { useEffect, useState } from "react";
import axios from "axios";

export default function App() {
  const [refreshApp, setRefreshApp] = useState(0);
  const apiUrl = '' // enter url where api is hosted

  const TweetBox = () => {
    const [tweet, setTweet] = useState("");

    const postTweet = (e) => {
      e.preventDefault();
      var fd = new FormData();
      fd.append("tweet", tweet);
      axios
        .post(apiUrl + "/posttweet.php", fd)
        .then(function (response) {
          if (response.data.response === "success") {
            setRefreshApp(refreshApp === 1 ? 0 : 1);
          } else alert("Error: " + response.data.result);
        })
        .catch(function (error) {
          console.log(error);
        });
    };

    return (
      <div
        style={{
          padding: "10px",
          background: "#4C9BE5",
          borderRadius: "5px",
          boxSizing: "border-box",
          overflow: "hidden"
        }}
      >
        <form onSubmit={postTweet}>
          <textarea
            style={{ width: "100%" }}
            onChange={(e) => setTweet(e.target.value)}
            value={tweet}
          ></textarea>
          <button
            type={"submit"}
            disabled={tweet.length > 0 ? false : true}
            style={{ float: "right" }}
          >
            Post
          </button>
        </form>
      </div>
    );
  };

  const TweetList = () => {
    const [tweets, setTweets] = useState([]);
    useEffect(() => {
      axios
        .get(apiUrl + "/gettweets.php")
        .then(function (response) {
          if (response.data.response === "success") {
            setTweets(response.data.result);
          }
        })
        .catch(function (error) {
          console.log(error);
        });
    });

    const editTweet = (e, tweet_id, newtweet) => {
      e.preventDefault();
      var fd = new FormData();
      fd.append("tweet", newtweet);
      fd.append("tweet_id", tweet_id);
      axios
        .post(apiUrl + "/updatetweet.php", fd)
        .then(function (response) {
          if (response.data.response === "success") {
            setRefreshApp(refreshApp === 1 ? 0 : 1);
          } else alert("Error: " + response.data.result);
        })
        .catch(function (error) {
          console.log(error);
        });
    };

    const deleteTweet = (e, tweet_id) => {
      e.preventDefault();
      var fd = new FormData();
      fd.append("tweet_id", tweet_id);
      axios
        .post(apiUrl + "/deletetweet.php", fd)
        .then(function (response) {
          if (response.data.response === "success") {
            setRefreshApp(refreshApp === 1 ? 0 : 1);
          } else alert("Error: " + response.data.result);
        })
        .catch(function (error) {
          console.log(error);
        });
    };

    const SingleTweet = (props) => {
      const [isEditing, setIsEditing] = useState(0);
      const [newtweet, setNewtweet] = useState(props.info.tweet);
      return (
        <div
          style={{
            padding: "10px",
            marginBottom: "10px",
            background: "#eee",
            overflow: "hidden"
          }}
        >
          {isEditing === 1 ? (
            <div>
              <form onSubmit={(e) => editTweet(e, props.info.id, newtweet)}>
                <textarea
                  style={{ width: "100%" }}
                  onChange={(e) => setNewtweet(e.target.value)}
                  value={newtweet}
                ></textarea>
                <button
                  type={"submit"}
                  disabled={newtweet.length > 0 ? false : true}
                  style={{ float: "right" }}
                >
                  Update
                </button>
              </form>
            </div>
          ) : (
            <div>
              <span style={{ color: "blue", fontWeight: "bold" }}>@User</span>
              &nbsp;
              {props.info.tweet}
              <div>
                <a
                  href="#"
                  onClick={(e) => {
                    e.preventDefault();
                    setIsEditing(1);
                  }}
                >
                  Edit
                </a>
                &nbsp;&nbsp;
                <a href="#" onClick={(e) => deleteTweet(e, props.info.id)}>
                  Delete
                </a>
                <span style={{ float: "right" }}>
                  {new Date(props.info.time * 1000).toLocaleString()}
                </span>
              </div>
            </div>
          )}
        </div>
      );
    };

    return (
      <div style={{ marginTop: "10px" }}>
        {tweets.map((tweet) => (
          <SingleTweet key={tweet.id + "_" + tweet.time} info={tweet} />
        ))}
      </div>
    );
  };

  return (
    <div style={{ width: "400px", margin: "auto" }}>
      <h2>Twitter Feed</h2>
      <TweetBox />
      <TweetList />
    </div>
  );
}

Creating Twitter CRUD database in MySql

For our application we just need a single table. Let’s call it “twitterfeed“. It will have a primary key, a text field for tweets and a timestamp to record tweet posting or updating time.

CREATE TABLE twitterfeed (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tweet VARCHAR(250) NOT NULL,
post_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)

Creating API Endpoints in Php

First of all we need to create a file to establish database connection. This file could be included in any other php script to access queries. Let’s call it mysql.inc.php.

<?php
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: Content-Type");

    $dbc = @mysqli_connect('localhost', REPLACE_WITH_DATABASE_USERNAME, REPLACE_WITH_DATABASE_PASSWORD, REPLACE_WITH_DATABASE_NAME);
    mysqli_set_charset($dbc, 'utf8');
    
    function mysql_safe($data)
    {
        global $dbc;
        return mysqli_real_escape_string($dbc, $data);
    }

Next we will create files for our CRUD operations. We will start with reading tweets. In our whole application we have called this file as gettweets.php.

<?php
    require('mysql.inc.php');
    
    $query = mysqli_query($dbc, "SELECT id, tweet, UNIX_TIMESTAMP(post_timestamp) AS post_timestamp FROM twitterfeed ORDER BY id DESC LIMIT 25");
    
    $tweets = array();
    while($query && $result=mysqli_fetch_array($query, MYSQLI_ASSOC)){
        array_push($tweets, array('id'=>$result['id'], 'tweet'=>$result['tweet'], 'time'=>$result['post_timestamp']));
    }
    
    echo json_encode(array('response' => 'success', 'result' => $tweets));

For this file –

POST / GET – none

Response – JSON(

{
   "response" : "success",
   "result"   : [
                    {
                       "id"    : INT(tweet id),
                       "tweet" : STRING("tweet text"),
                       "time"  : TIMESTAMP,
                    },
                    ...
                 ]
}

)

Endpoint for posting tweets is posttweet.php. It will accept a POST request of tweet text.

<?php
    require('mysql.inc.php');
    
    if(isset($_POST['tweet'])){
        if(mb_strlen(trim($_POST['tweet'])) > 0){
            $query = mysqli_query($dbc, "INSERT INTO twitterfeed(tweet) VALUES('".mysql_safe(trim($_POST['tweet']))."')");
            
            if(mysqli_affected_rows($dbc) > 0)
                echo json_encode(array('response' => 'success'));
            else
                echo json_encode(array('response' => 'error', 'result' => 'Could not save your tweet'));
                
        } else {
            echo json_encode(array('response' => 'error', 'result' => 'Please write something before posting'));
        }
    } else {
        echo json_encode(array('response' => 'error', 'result' => 'Invalid Activity'));
    }

POST – {"tweet" : "tweet text"}

Response – JSON(

{
   "response" : "success" | "error",
   "result"   : "Could not save your tweet" | "Please write something before posting" | "Invalid Activity"
}

)

Text of the tweets could be updated using updatetweet.php. Here is the code –

<?php
    require('mysql.inc.php');
    
    if(isset($_POST['tweet_id']) && isset($_POST['tweet'])){
        if(is_numeric($_POST['tweet_id']) && mb_strlen(trim($_POST['tweet'])) > 0){
            $query = mysqli_query($dbc, "UPDATE twitterfeed SET tweet = '".mysql_safe(trim($_POST['tweet']))."' WHERE id=".mysql_safe($_POST['tweet_id']));
            
            if(mysqli_affected_rows($dbc) > 0)
                echo json_encode(array('response' => 'success'));
            else
                echo json_encode(array('response' => 'error', 'result' => 'Could not update your tweet'));
                
        } else {
            echo json_encode(array('response' => 'error', 'result' => 'Invalid Activity'));
        }
    } else {
        echo json_encode(array('response' => 'error', 'result' => 'Invalid Activity'));
    }

POST –

{
   "tweet_id" : INT(id of tweet to be updated),
   "tweet"    : STRING("updated tweet text"),
}

Response – JSON(

{
   "response" : "success" | "error",
   "result"   : "Could not update your tweet" | "Invalid Activity"
}

)

Last but not the least, deletion, could be done by deletetweet.php. We will send the id of tweet which is to be deleted.

<?php
    require('mysql.inc.php');
    
    if(isset($_POST['tweet_id'])){
        if(is_numeric($_POST['tweet_id'])){
            $query = mysqli_query($dbc, "DELETE FROM twitterfeed WHERE id=".mysql_safe($_POST['tweet_id']));
            
            if(mysqli_affected_rows($dbc) > 0)
                echo json_encode(array('response' => 'success'));
            else
                echo json_encode(array('response' => 'error', 'result' => 'Could not delete your tweet'));
                
        } else {
            echo json_encode(array('response' => 'error', 'result' => 'Invalid Activity'));
        }
    } else {
        echo json_encode(array('response' => 'error', 'result' => 'Invalid Activity'));
    }

POST – { "tweet_id" : id of tweed to be deleted}

Response – JSON(

{
   "response" : "success" | "error",
   "result"   : "Could not delete your tweet" | "Invalid Activity"
}

)

    I learned CRUD in 10 Minutes.

Click to Tweet this

Live Demo

Open Live Demo