In this lesson, we will create the Add Student screen for our Student Management mobile application using React Native, TypeScript, Axios, and the PHP REST API.
The screen will allow an authenticated user to enter student information and send it to the PHP REST API. The PHP API will validate the data and insert the new student into the MySQL database.
Add Student Screen
↓
Enter Student Information
↓
Client Validation
↓
Axios POST Request
↓
JWT Authentication
↓
PHP REST API
↓
Server Validation
↓
PDO INSERT
↓
MySQL Database
↓
JSON Response
↓
Student List
Our project student table contains fields such as:
These fields will be collected through the React Native form.
interface AddStudentRequest {
name: string;
email: string;
mobile: string;
course: string;
address: string;
}
This interface ensures that the request contains the expected student fields.
interface AddStudentResponse {
success: boolean;
message: string;
data?: Student;
}
The API can return the newly created student inside the
data property.
import React, {
useState
} from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
ScrollView,
ActivityIndicator
} from "react-native";
const [name, setName] =
useState("");
const [email, setEmail] =
useState("");
const [mobile, setMobile] =
useState("");
const [course, setCourse] =
useState("");
const [address, setAddress] =
useState("");
const [loading, setLoading] =
useState(false);
const [error, setError] =
useState("");
const [success, setSuccess] =
useState("");
<TextInput
placeholder="Enter student name"
value={name}
onChangeText={setName}
/>
The value entered by the user is stored in the name
state.
<TextInput
placeholder="Enter email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextInput
placeholder="Enter mobile number"
value={mobile}
onChangeText={setMobile}
keyboardType="phone-pad"
/>
<TextInput
placeholder="Enter course"
value={course}
onChangeText={setCourse}
/>
Later, this field can also be replaced with a Picker or dropdown.
<TextInput
placeholder="Enter address"
value={address}
onChangeText={setAddress}
multiline
/>
if (
!name ||
!email ||
!mobile ||
!course
) {
setError(
"Please fill all required fields."
);
return;
}
Client-side validation provides immediate feedback before sending the request.
const emailPattern =
/^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(email)) {
setError(
"Enter a valid email address."
);
return;
}
if (
mobile.length !== 10
) {
setError(
"Mobile number must contain 10 digits."
);
return;
}
Client validation improves the user experience, but the server must also validate the mobile number.
const studentData:
AddStudentRequest = {
name: name.trim(),
email: email.trim(),
mobile: mobile.trim(),
course: course.trim(),
address: address.trim()
};
const response =
await api.post<AddStudentResponse>(
"/students.php",
studentData
);
The request sends the student data to the protected Student API.
The Student API is protected. The Axios instance should send the JWT token in the Authorization header.
Authorization:
Bearer YOUR_JWT_TOKEN
If an Axios interceptor is already configured, the token can be added automatically to every protected request.
$input = json_decode(
file_get_contents("php://input"),
true
);
$name =
trim($input['name'] ?? '');
$email =
trim($input['email'] ?? '');
$mobile =
trim($input['mobile'] ?? '');
$course =
trim($input['course'] ?? '');
$address =
trim($input['address'] ?? '');
if (
$name === '' ||
$email === '' ||
$mobile === '' ||
$course === ''
) {
http_response_code(422);
echo json_encode([
"success" => false,
"message" =>
"Required fields are missing."
]);
exit;
}
Server-side validation is required because users can bypass mobile application validation.
$stmt = $pdo->prepare(
"INSERT INTO students
(name, email, mobile, course, address)
VALUES (?, ?, ?, ?, ?)"
);
$stmt->execute([
$name,
$email,
$mobile,
$course,
$address
]);
Prepared statements should be used for database inserts.
http_response_code(201);
echo json_encode([
"success" => true,
"message" =>
"Student added successfully.",
"data" => [
"id" => $pdo->lastInsertId(),
"name" => $name,
"email" => $email,
"mobile" => $mobile,
"course" => $course,
"address" => $address
]
]);
HTTP 201 indicates that a new resource was successfully created.
If email addresses are unique in the database, the API should detect an existing email before insertion.
$stmt = $pdo->prepare(
"SELECT id
FROM students
WHERE email = ?"
);
$stmt->execute([$email]);
if ($stmt->fetch()) {
http_response_code(409);
echo json_encode([
"success" => false,
"message" =>
"Email already exists."
]);
exit;
}
const addStudent =
async () => {
try {
setLoading(true);
setError("");
setSuccess("");
if (
!name ||
!email ||
!mobile ||
!course
) {
setError(
"Please fill all required fields."
);
return;
}
const studentData:
AddStudentRequest = {
name: name.trim(),
email: email.trim(),
mobile: mobile.trim(),
course: course.trim(),
address: address.trim()
};
const response =
await api.post<AddStudentResponse>(
"/students.php",
studentData
);
setSuccess(
response.data.message
);
setName("");
setEmail("");
setMobile("");
setCourse("");
setAddress("");
} catch (error) {
setError(
"Unable to add student."
);
} finally {
setLoading(false);
}
};
catch (error) {
if (
axios.isAxiosError(error)
) {
if (
error.response?.status === 401
) {
setError(
"Session expired."
);
} else if (
error.response?.status === 409
) {
setError(
"Email already exists."
);
} else if (
error.response?.status === 422
) {
setError(
error.response.data.message
|| "Invalid data."
);
} else {
setError(
"Server error."
);
}
} else {
setError(
"Network error."
);
}
}
{error !== "" && (
<Text>
{error}
</Text>
)}
{success !== "" && (
<Text>
{success}
</Text>
)}
The user should clearly know whether the student was added successfully or the request failed.
const AddStudentScreen =
() => {
const [name, setName] =
useState("");
const [email, setEmail] =
useState("");
const [mobile, setMobile] =
useState("");
const [course, setCourse] =
useState("");
const [address, setAddress] =
useState("");
const [loading, setLoading] =
useState(false);
const [error, setError] =
useState("");
const [success, setSuccess] =
useState("");
const addStudent =
async () => {
try {
setError("");
setSuccess("");
if (
!name ||
!email ||
!mobile ||
!course
) {
setError(
"Please fill all required fields."
);
return;
}
const studentData:
AddStudentRequest = {
name: name.trim(),
email: email.trim(),
mobile: mobile.trim(),
course: course.trim(),
address: address.trim()
};
setLoading(true);
const response =
await api.post<AddStudentResponse>(
"/students.php",
studentData
);
setSuccess(
response.data.message
);
setName("");
setEmail("");
setMobile("");
setCourse("");
setAddress("");
} catch (error) {
setError(
"Unable to add student."
);
} finally {
setLoading(false);
}
};
return (
<ScrollView>
<Text>
Add Student
</Text>
<TextInput
placeholder="Name"
value={name}
onChangeText={setName}
/>
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
/>
<TextInput
placeholder="Mobile"
value={mobile}
onChangeText={setMobile}
keyboardType="phone-pad"
/>
<TextInput
placeholder="Course"
value={course}
onChangeText={setCourse}
/>
<TextInput
placeholder="Address"
value={address}
onChangeText={setAddress}
multiline
/>
{error !== "" && (
<Text>
{error}
</Text>
)}
{success !== "" && (
<Text>
{success}
</Text>
)}
{loading && (
<ActivityIndicator />
)}
<TouchableOpacity
onPress={addStudent}
>
<Text>
Add Student
</Text>
</TouchableOpacity>
</ScrollView>
);
};
After successfully adding a student, the application can navigate back to the Student List screen.
navigation.navigate(
"StudentList"
);
The Student List can then refresh its data to display the newly created student.
The application should avoid sending multiple requests when the user taps the Add Student button repeatedly.
if (loading) {
return;
}
setLoading(true);
The button can also provide visual feedback while the request is being processed.
Add Student Screen
↓
TypeScript Form State
↓
Client Validation
↓
Axios POST
↓
JWT Authorization
↓
PHP API
↓
Authentication
↓
Server Validation
↓
Duplicate Check
↓
PDO Prepared Statement
↓
MySQL INSERT
↓
HTTP 201
↓
React Native Success
↓
Student List
The Add Student screen allows the application to create a new student through the protected REST API.
Question: Which HTTP status code is normally returned when a new student is successfully created?