In this lesson, we will create the user registration API for our Student Management mobile application.
The React Native application will send registration information to the PHP REST API. The PHP API will validate the data, hash the password, and store the new user in the MySQL database.
The registration process will follow this flow:
React Native Registration Form
↓
Axios POST
↓
PHP API
↓
Validate
↓
Check Duplicate Email
↓
Hash Password
↓
MySQL
↓
JSON Response
We can create a PHP file named:
register.php
The mobile application will send a POST request:
POST /api/register.php
Registration data will be sent in JSON format.
The API will receive information such as:
{
"name": "Rahul Kumar",
"email": "rahul@example.com",
"password": "secret123"
}
The password will never be stored directly in this form.
Because the API returns JSON, we should set the response Content-Type.
header(
"Content-Type: application/json"
);
This tells the client that the server response contains JSON.
The registration API needs a PDO connection to the
student_management database.
require_once '../config/database.php';
The database connection should remain on the server and should never be placed inside the React Native application.
Registration creates a new record, so the API should accept POST requests.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode([
"success" => false,
"message" => "Method not allowed"
]);
exit;
}
JSON request data can be read using php://input.
$input = json_decode(
file_get_contents("php://input"),
true
);
The second argument true converts the JSON object into
a PHP associative array.
$name = trim($input['name'] ?? '');
$email = trim($input['email'] ?? '');
$password = $input['password'] ?? '';
Using the null coalescing operator helps prevent undefined index errors when a field is missing.
Registration should not continue if required fields are missing.
if ($name === '' ||
$email === '' ||
$password === '') {
http_response_code(422);
echo json_encode([
"success" => false,
"message" => "All fields are required"
]);
exit;
}
PHP provides filter_var() for basic email validation.
if (!filter_var(
$email,
FILTER_VALIDATE_EMAIL
)) {
http_response_code(422);
echo json_encode([
"success" => false,
"message" => "Invalid email address"
]);
exit;
}
A minimum password length should be enforced by the server.
if (strlen($password) < 6) {
http_response_code(422);
echo json_encode([
"success" => false,
"message" =>
"Password must be at least 6 characters"
]);
exit;
}
The email column is unique in our database. We should check whether the email already exists before inserting a new user.
$stmt = $pdo->prepare(
"SELECT id
FROM users
WHERE email = ?"
);
$stmt->execute([$email]);
if ($stmt->fetch()) {
http_response_code(409);
echo json_encode([
"success" => false,
"message" => "Email already registered"
]);
exit;
}
HTTP status code 409 Conflict is appropriate when the
submitted data conflicts with existing data.
In this project, a duplicate email is a good example.
409 Conflict
↓
Email already exists
Never save the user's original password.
$passwordHash = password_hash(
$password,
PASSWORD_DEFAULT
);
The generated hash should be stored in the database.
$stmt = $pdo->prepare(
"INSERT INTO users
(name, email, password, role)
VALUES (?, ?, ?, ?)"
);
$stmt->execute([
$name,
$email,
$passwordHash,
'user'
]);
The prepared statement protects the database query from SQL injection.
After inserting the user, we can retrieve the generated ID.
$userId = $pdo->lastInsertId();
This ID can be included in a safe registration response.
A successful resource creation can return HTTP status
201 Created.
http_response_code(201);
echo json_encode([
"success" => true,
"message" => "Registration successful",
"data" => [
"id" => $userId,
"name" => $name,
"email" => $email
]
]);
The registration response should not contain the password or password hash.
Safe response:
{
"id": 1,
"name": "Rahul Kumar",
"email": "rahul@example.com"
}
The password should remain private.
Database operations should be protected with exception handling.
try {
// Database operation
} catch (PDOException $e) {
error_log($e->getMessage());
http_response_code(500);
echo json_encode([
"success" => false,
"message" => "Server error"
]);
}
Detailed database errors should not be exposed to mobile users.
<?php
header(
"Content-Type: application/json"
);
require_once '../config/database.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode([
"success" => false,
"message" => "Method not allowed"
]);
exit;
}
$input = json_decode(
file_get_contents("php://input"),
true
);
$name = trim($input['name'] ?? '');
$email = trim($input['email'] ?? '');
$password = $input['password'] ?? '';
if ($name === '' ||
$email === '' ||
$password === '') {
http_response_code(422);
echo json_encode([
"success" => false,
"message" => "All fields are required"
]);
exit;
}
if (!filter_var(
$email,
FILTER_VALIDATE_EMAIL
)) {
http_response_code(422);
echo json_encode([
"success" => false,
"message" => "Invalid email address"
]);
exit;
}
if (strlen($password) < 6) {
http_response_code(422);
echo json_encode([
"success" => false,
"message" =>
"Password must be at least 6 characters"
]);
exit;
}
$stmt = $pdo->prepare(
"SELECT id FROM users WHERE email = ?"
);
$stmt->execute([$email]);
if ($stmt->fetch()) {
http_response_code(409);
echo json_encode([
"success" => false,
"message" => "Email already registered"
]);
exit;
}
$passwordHash = password_hash(
$password,
PASSWORD_DEFAULT
);
$stmt = $pdo->prepare(
"INSERT INTO users
(name, email, password, role)
VALUES (?, ?, ?, ?)"
);
$stmt->execute([
$name,
$email,
$passwordHash,
'user'
]);
$userId = $pdo->lastInsertId();
http_response_code(201);
echo json_encode([
"success" => true,
"message" => "Registration successful",
"data" => [
"id" => $userId,
"name" => $name,
"email" => $email
]
]);
The mobile application can contain three main input fields.
Name
Email
Password
React Native state can store their values.
const [name, setName] =
useState("");
const [email, setEmail] =
useState("");
const [password, setPassword] =
useState("");
TypeScript can define the structure of the registration request.
interface RegisterRequest {
name: string;
email: string;
password: string;
}
This helps prevent incorrect request data from being sent.
interface RegisterResponse {
success: boolean;
message: string;
data?: {
id: number;
name: string;
email: string;
};
}
The data property is optional because an unsuccessful
response may contain only an error message.
const response =
await api.post<RegisterResponse>(
"/register.php",
{
name,
email,
password
}
);
console.log(response.data);
Axios sends the object as JSON when the appropriate content type is configured.
try {
const response =
await api.post<RegisterResponse>(
"/register.php",
{
name,
email,
password
}
);
console.log(
response.data.message
);
} catch (error) {
console.log(
"Registration failed"
);
}
A reusable Axios error handler can later provide better messages for validation, duplicate email, and network errors.
Before connecting React Native, test the registration API using Postman.
Method: POST
URL:
https://example.com/api/register.php
Body → raw → JSON:
{
"name": "Rahul Kumar",
"email": "rahul@example.com",
"password": "secret123"
}
Success:
{
"success": true,
"message": "Registration successful",
"data": {
"id": 1,
"name": "Rahul Kumar",
"email": "rahul@example.com"
}
}
Duplicate email:
{
"success": false,
"message": "Email already registered"
}
password_hash().Registration Screen
↓
TypeScript Validation
↓
Axios POST
↓
PHP register.php
↓
Read JSON
↓
Validate Input
↓
Check Duplicate Email
↓
password_hash()
↓
PDO INSERT
↓
HTTP 201
↓
JSON Response
↓
React Native
We have now designed the complete registration process for the Student Management application.
In the next lesson, we will create the user login API that verifies the registered user's email and password.
php://input.password_hash().Question: Which PHP function should be used to securely hash a user's password before storing it in MySQL?