A login API allows a React Native application to send user credentials to a backend server and receive an authentication response. In this lesson, we will connect a React Native login screen with a PHP REST API using Axios and TypeScript.
A login API is an endpoint that receives login information and verifies the user.
POST /api/login.php
The API can receive an email and password and return a JWT or another authentication token after successful verification.
Login Screen
↓
Email + Password
↓
Axios POST
↓
PHP Login API
↓
Verify Credentials
↓
Generate Token
↓
Login Response
↓
React Native
↓
Store Authentication State
The React Native application can send JSON data to the PHP API.
{
"email": "student@example.com",
"password": "123456"
}
The request should use the Content-Type: application/json
header.
interface LoginRequest {
email: string;
password: string;
}
This interface describes the data sent from React Native to the API.
interface User {
id: number;
name: string;
email: string;
role: "admin" | "teacher" | "student";
}
interface LoginResponse {
success: boolean;
message: string;
token: string;
user: User;
}
import axios from "axios";
const api = axios.create({
baseURL: "https://example.com/api",
headers: {
"Content-Type": "application/json"
}
});
An Axios instance provides a central place for API configuration.
const login = async (
data: LoginRequest
): Promise<LoginResponse> => {
const response =
await api.post<LoginResponse>(
"/login.php",
data
);
return response.data;
};
const [email, setEmail] =
useState("");
const [password, setPassword] =
useState("");
const [loading, setLoading] =
useState(false);
const [error, setError] =
useState("");
const data: LoginRequest = {
email: email,
password: password
};
Because the object uses the LoginRequest interface,
TypeScript can check the structure of the request.
const result =
await login({
email,
password
});
console.log(result);
The returned value follows the LoginResponse interface.
if (result.success) {
console.log(
"Login successful"
);
} else {
console.log(
result.message
);
}
if (result.success) {
const token =
result.token;
console.log(token);
}
The token can later be used for protected API requests.
if (result.success) {
console.log(
result.user.name
);
console.log(
result.user.email
);
console.log(
result.user.role
);
}
const handleLogin = async () => {
try {
setLoading(true);
setError("");
const result =
await login({
email,
password
});
console.log(result);
} catch (error) {
setError(
"Unable to login"
);
} finally {
setLoading(false);
}
};
import axios from "axios";
try {
await login({
email,
password
});
} catch (error) {
if (axios.isAxiosError(error)) {
console.log(
error.response?.data
);
}
}
The PHP API can return HTTP 401 when the credentials are invalid.
{
"success": false,
"message": "Invalid email or password"
}
The application should show a safe message to the user instead of exposing internal server information.
PHP can read the JSON request body using php://input.
$input = json_decode(
file_get_contents("php://input"),
true
);
$email = trim($input["email"] ?? "");
$password = $input["password"] ?? "";
if ($email === "" || $password === "") {
http_response_code(422);
echo json_encode([
"success" => false,
"message" => "Email and password are required"
]);
exit;
}
Server-side validation is required even if the React Native screen already validates the form.
$stmt = $pdo->prepare(
"SELECT id, name, email, password, role
FROM users
WHERE email = ?"
);
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
A prepared statement helps prevent SQL injection.
if (!$user ||
!password_verify(
$password,
$user["password"]
)) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Invalid email or password"
]);
exit;
}
The server should store passwords as secure hashes, not plain text.
$response = [
"success" => true,
"message" => "Login successful",
"token" => $token,
"user" => [
"id" => (int)$user["id"],
"name" => $user["name"],
"email" => $user["email"],
"role" => $user["role"]
]
];
echo json_encode($response);
const response =
await api.get(
"/profile.php",
{
headers: {
Authorization:
`Bearer ${token}`
}
}
);
The token received during login is sent with later protected requests.
interface AuthState {
token: string | null;
user: User | null;
isAuthenticated: boolean;
}
setAuth({
token: result.token,
user: result.user,
isAuthenticated: true
});
After successful authentication, the application can navigate to the appropriate protected screen.
if (result.success) {
setAuth({
token: result.token,
user: result.user,
isAuthenticated: true
});
navigation.navigate(
"Home"
);
}
Navigation is only a UI decision; the server must still protect API endpoints.
interface LoginRequest {
email: string;
password: string;
}
interface User {
id: number;
name: string;
email: string;
role: "admin" | "teacher" | "student";
}
interface LoginResponse {
success: boolean;
message: string;
token: string;
user: User;
}
export const login =
async (
data: LoginRequest
): Promise<LoginResponse> => {
const response =
await api.post<LoginResponse>(
"/login.php",
data
);
return response.data;
};
const handleLogin = async () => {
if (!email || !password) {
setError(
"Enter email and password"
);
return;
}
try {
setLoading(true);
setError("");
const result =
await login({
email,
password
});
if (result.success) {
setAuth({
token: result.token,
user: result.user,
isAuthenticated: true
});
navigation.navigate(
"Home"
);
} else {
setError(
result.message
);
}
} catch (error) {
setError(
"Login request failed"
);
} finally {
setLoading(false);
}
};
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
/>
<TextInput
placeholder="Password"
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title="Login"
onPress={handleLogin}
/>
The form collects the credentials and sends them to the login service.
const handleLogin = async () => {
try {
setLoading(true);
setError("");
const data: LoginRequest = {
email,
password
};
const result =
await login(data);
if (result.success) {
console.log(
"User:",
result.user.name
);
console.log(
"Token:",
result.token
);
setAuth({
token: result.token,
user: result.user,
isAuthenticated: true
});
} else {
setError(
result.message
);
}
} catch (error) {
setError(
"Unable to connect to server"
);
} finally {
setLoading(false);
}
};
React Native
│
│ LoginRequest
↓
Axios
│
│ POST /login.php
↓
PHP REST API
│
├── Validate Input
├── Find User
├── Verify Password
└── Create JWT
│
↓
LoginResponse
│
├── success
├── message
├── token
└── user
│
↓
React Native AuthState
│
↓
Protected Screens
1. User enters email and password
2. React Native validates the form
3. Axios sends POST request
4. PHP reads JSON request
5. PHP validates input
6. PHP finds the user
7. PHP verifies password hash
8. PHP creates JWT/token
9. PHP returns LoginResponse
10. React Native updates AuthState
11. Token is used for protected APIs
12. PHP verifies token on protected requests
This is the basic authentication flow used by a React Native application communicating with a PHP REST API. TypeScript interfaces make the request and response structures predictable while the server remains responsible for securely authenticating and authorizing the user.
Question: Which HTTP method is normally used to send login credentials to a login API?