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 –
- Creating a new tweet (Create).
- Listing all the previous tweets (Read).
- Editing any existing tweet (Update).
- 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 –
- posttweet.php – for adding new tweet.
- gettweets.php – for fetching all added tweets.
- updatetweet.php – for editing a tweet.
- 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 –
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> {props.info.tweet} <div> <a href="#" onClick={(e) => { e.preventDefault(); setIsEditing(1); }} > Edit </a> <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 –
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> {props.info.tweet} <div> <a href="#" onClick={(e) => { e.preventDefault(); setIsEditing(1); }} > Edit </a> <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" }
)