Skip to main content

🔌 Khanaa API Integration Guide (Frontend & Mobile)

Welcome to the Khanaa Frontend Integration Guide. This document explains how to connect your Next.js and React Native applications to the Khanaa Core API.

Our backend uses a Unified Authentication System. This means Customers, Riders, and Vendors all authenticate using the exact same endpoints. Your app's routing logic determines which screens to show based on their database role.


1. Environment & Base URL Setup

Do NOT hardcode the API URL in your components. We use environment variables so the apps can seamlessly switch between the live server and local testing environments.

Create or update your .env file:

# For Next.js (Web):
NEXT_PUBLIC_API_URL=https://api.khanaa.in

# For React Native (Expo):
EXPO_PUBLIC_API_URL=https://api.khanaa.in

# Note: If testing locally with the backend team, replace the above with the ngrok URL they provide.

2. Centralized API Client (Axios)

To avoid repeating code, configure an Axios instance globally. This ensures every request goes to the correct URL and automatically attaches the JWT token.

Create src/services/apiClient.ts (or .js):

import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage'; // Use localStorage for Next.js

export const apiClient = axios.create({
baseURL: process.env.EXPO_PUBLIC_API_URL || process.env.NEXT_PUBLIC_API_URL,
timeout: 10000,
});

// Automatically inject the Khanaa JWT Token into every request
apiClient.interceptors.request.use(
async (config) => {
const token = await AsyncStorage.getItem('khanaa_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);

// Global error handler for Expired Tokens
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Token is invalid or expired. Clear storage and redirect to Login.
await AsyncStorage.removeItem('khanaa_token');
// trigger logout / navigation logic here
}
return Promise.reject(error);
}
);

3. Authentication Endpoints

We support three primary login methods. All successful logins return the exact same response structure.

A. Mobile OTP (Primary)

  • Request OTP: POST /auth/send-otp
    Payload:
{ "phone": "+919876543210" }
  • Verify OTP: POST /auth/verify-otp
    Payload:
{ "phone": "+919876543210", "otp": "123456" }

B. Email & Password

  • Register: POST /auth/email/register
{ "email": "x@x.com", "password": "pass" }
  • Login: POST /auth/email/login
{ "email": "x@x.com", "password": "pass", "rememberMe": true }

C. Google SSO

  • Login/Register: POST /auth/google
{ "idToken": "eyJhbGci..." }

Note: Obtain the idToken using the React Native Google SDK. Do NOT implement web redirects on mobile.


Expected API Response (All Login Methods)

{
"message": "Authentication successful",
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "uuid-1234",
"phone": "+919876543210",
"email": "user@example.com",
"role": "CUSTOMER"
}
}

4. The "Gatekeeper" Routing Logic (Crucial)

After a successful login, you MUST save the accessToken to secure storage. Then, evaluate the user.role to determine the user's destination.

Example (Vendor App):

const handleLoginSuccess = async (response) => {
// 1. Save the token
await AsyncStorage.setItem('khanaa_token', response.accessToken);

// 2. Route based on role
if (response.user.role === 'VENDOR') {
navigation.replace('VendorDashboard');
}
else if (response.user.role === 'CUSTOMER') {
navigation.replace('KycUploadScreen');
}
else {
alert('Unauthorized app access.');
await AsyncStorage.removeItem('khanaa_token');
}
};

5. Password Management & Security Endpoints

Unauthenticated (No JWT required)

  • Forgot Password: POST /auth/password/forgot
{ "email": "x@x.com" }
  • Reset Password: POST /auth/password/reset
{ "token": "string", "newPassword": "pass" }

Authenticated (Requires JWT)

  • Change Password: POST /auth/password/change
{ "oldPassword": "pass", "newPassword": "pass" }
  • Fetch Profile: GET /auth/me

6. Calling Protected Routes Example

Because you configured the apiClient, calling protected routes becomes simple.

import { apiClient } from '../services/apiClient';

// Example: Fetch user profile
const fetchUserProfile = async () => {
try {
const response = await apiClient.get('/auth/me');
console.log("User Data:", response.data);
} catch (error) {
console.error("Failed to fetch profile:", error.response?.data?.message);
}
};

7. API Documentation

Interactive Swagger Docs:

👉 https://api.khanaa.in/api/docs