In this lesson, we will create the Search Student API for our Student Management mobile application.
The React Native application will send a search keyword to the PHP REST API. The PHP API will search the MySQL database using a prepared statement and return matching students as JSON.
React Native Search Box
↓
Search Keyword
↓
Axios GET Request
↓
students.php?search=rahul
↓
JWT Verification
↓
PHP Search Logic
↓
MySQL LIKE Query
↓
JSON Response
↓
React Native FlatList
The search keyword can be sent using a query parameter.
GET /api/students.php?search=rahul
Here, search contains the keyword entered by the user.
A query parameter is a value added after the question mark in a URL.
?search=rahul
Multiple query parameters can also be used.
?search=rahul&page=1&limit=10
$search = trim(
$_GET['search'] ?? ''
);
The PHP API reads the search keyword from the URL.
The API can return all students when no search keyword is provided, depending on the endpoint design.
if ($search === '') {
// Return all students
} else {
// Search students
}
In our project, this allows the same endpoint to support both the student list and search functionality.
We can search more than one column.
SELECT *
FROM students
WHERE name LIKE ?
OR email LIKE ?
OR mobile LIKE ?
OR course LIKE ?
This allows users to search using name, email, mobile number, or course.
The SQL LIKE operator is commonly used for text
searching.
WHERE name LIKE ?
The percentage symbol can be used to find a keyword anywhere inside the column value.
%rahul%
$keyword = "%{$search}%";
$stmt = $pdo->prepare(
"SELECT *
FROM students
WHERE name LIKE ?
OR email LIKE ?
OR mobile LIKE ?
OR course LIKE ?"
);
$stmt->execute([
$keyword,
$keyword,
$keyword,
$keyword
]);
Prepared statements should be used instead of directly inserting user input into SQL.
Search input comes directly from the user. It should never be concatenated into an SQL query.
// Avoid
$sql =
"SELECT * FROM students
WHERE name LIKE '%$search%'";
Instead, use placeholders and PDO prepared statements.
students =
$stmt->fetchAll(
PDO::FETCH_ASSOC
);
The matching database rows are converted into a PHP array.
echo json_encode([
"success" => true,
"data" => $students
]);
The PHP API sends the search results as JSON.
{
"success": true,
"data": [
{
"id": 5,
"name": "Rahul Kumar",
"email": "rahul@example.com",
"mobile": "9876543211",
"course": "React Native"
}
]
}
Student data should not be exposed through an unprotected administrative API.
Authorization:
Bearer YOUR_JWT_TOKEN
The API should verify the JWT before returning protected student data.
$headers = getallheaders();
$authorization =
$headers['Authorization']
?? '';
The server retrieves the Authorization header sent by Axios.
if (
!preg_match(
'/Bearer\s(\S+)/',
$authorization,
$matches
)
) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" =>
"Authentication required"
]);
exit;
}
$token = $matches[1];
After extracting the token, the API should verify it using the same JWT secret and algorithm used during login.
$search = trim(
$_GET['search'] ?? ''
);
if ($search === '') {
$stmt = $pdo->prepare(
"SELECT id, name, email,
mobile, course, address
FROM students
ORDER BY id DESC"
);
$stmt->execute();
} else {
$keyword = "%{$search}%";
$stmt = $pdo->prepare(
"SELECT id, name, email,
mobile, course, address
FROM students
WHERE name LIKE ?
OR email LIKE ?
OR mobile LIKE ?
OR course LIKE ?
ORDER BY id DESC"
);
$stmt->execute([
$keyword,
$keyword,
$keyword,
$keyword
]);
}
$students = $stmt->fetchAll(
PDO::FETCH_ASSOC
);
echo json_encode([
"success" => true,
"data" => $students
]);
<?php
header(
"Content-Type: application/json"
);
require_once '../config/database.php';
require_once __DIR__ .
'/vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secretKey =
'CHANGE_THIS_TO_A_LONG_RANDOM_SECRET';
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
echo json_encode([
"success" => false,
"message" => "Method not allowed"
]);
exit;
}
$headers = getallheaders();
$authorization =
$headers['Authorization']
?? '';
if (
!preg_match(
'/Bearer\s(\S+)/',
$authorization,
$matches
)
) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" =>
"Authentication required"
]);
exit;
}
$token = $matches[1];
try {
JWT::decode(
$token,
new Key(
$secretKey,
'HS256'
)
);
$search = trim(
$_GET['search'] ?? ''
);
if ($search === '') {
$stmt = $pdo->prepare(
"SELECT id, name, email,
mobile, course, address
FROM students
ORDER BY id DESC"
);
$stmt->execute();
} else {
$keyword = "%{$search}%";
$stmt = $pdo->prepare(
"SELECT id, name, email,
mobile, course, address
FROM students
WHERE name LIKE ?
OR email LIKE ?
OR mobile LIKE ?
OR course LIKE ?
ORDER BY id DESC"
);
$stmt->execute([
$keyword,
$keyword,
$keyword,
$keyword
]);
}
$students = $stmt->fetchAll(
PDO::FETCH_ASSOC
);
http_response_code(200);
echo json_encode([
"success" => true,
"data" => $students
]);
} catch (Exception $e) {
error_log($e->getMessage());
http_response_code(401);
echo json_encode([
"success" => false,
"message" =>
"Invalid or expired token"
]);
}
The Student List screen can contain a TextInput for entering the search keyword.
<TextInput
placeholder="Search student..."
value={search}
onChangeText={setSearch}
/>
Whenever the search value changes, the application can request matching students from the API.
const searchStudents =
async (keyword: string) => {
try {
const response =
await api.get(
"/students.php",
{
params: {
search: keyword
}
}
);
setStudents(
response.data.data
);
} catch (error) {
console.log(
"Search failed"
);
}
};
Axios provides a convenient params option for query
parameters.
api.get(
"/students.php",
{
params: {
search: "rahul"
}
}
);
Axios converts this into a URL similar to:
/students.php?search=rahul
interface Student {
id: number;
name: string;
email: string;
mobile: string;
course: string;
address: string;
}
This interface represents one student returned by the API.
interface StudentResponse {
success: boolean;
data: Student[];
}
We can use this interface to type the Axios response.
const response =
await api.get<StudentResponse>(
"/students.php",
{
params: {
search: keyword
}
}
);
<FlatList
data={students}
keyExtractor={item =>
item.id.toString()
}
renderItem={({ item }) => (
<View>
<Text>
{item.name}
</Text>
<Text>
{item.course}
</Text>
<Text>
{item.mobile}
</Text>
</View>
)}
/>
FlatList is suitable for displaying a list of student records.
{loading && (
<ActivityIndicator />
)}
{!loading &&
students.length === 0 && (
<Text>
No students found
</Text>
)}
Loading and empty states provide better feedback to the user.
Large student databases should not return thousands of records in a single request.
/students.php
?search=rahul
&page=1
&limit=10
The API can combine search with pagination.
The previous project lesson on pagination can be used to calculate the SQL offset and return pagination metadata.
Method: GET
URL:
https://example.com/api/students.php?search=rahul
Header:
Authorization: Bearer YOUR_JWT_TOKEN
Example Response:
{
"success": true,
"data": [
{
"id": 5,
"name": "Rahul Kumar",
"email": "rahul@example.com",
"mobile": "9876543211",
"course": "React Native",
"address": "Patna"
}
]
}
Searching with LIKE '%keyword%' is convenient, but large
databases may require additional optimization.
Useful techniques include:
Student List Screen
↓
Search TextInput
↓
Search Keyword
↓
Axios GET
↓
JWT Authorization
↓
PHP Search API
↓
Validate Request
↓
PDO Prepared Statement
↓
MySQL LIKE Search
↓
JSON Response
↓
TypeScript Student[]
↓
FlatList
↓
Display Results
The Search Student API allows the React Native application to find students without downloading the complete database.
$_GET.LIKE can perform partial matching.?search=rahul.LIKE for partial text matching.params option.Question: Which SQL operator is commonly used for partial text searching?