PVR Tech Studio
Dev data api

Data & API Integration

The shared API contract, the bundled Express mock API, the request/response shape, and how to point any edition at your own backend.

4 min read
Updated June 21, 2026

Every edition of Taxi CRM 2026 fetches its list and form data over HTTP. The endpoint surface is defined once in the shared api-contract.mjs (exported by @taxi-crm/design-system) and mirrored by the bundled Express mock API in services/api. You point any edition at your own backend by changing one base URL.

The shared API contract

packages/design-system/api-contract.mjs is the single source of truth for the API surface. It is framework-agnostic (no window/DOM access) and exports three things:

import { endpoints, headers, createApi } from '@taxi-crm/design-system/api-contract';
 
const api = createApi();                         // base '/api' (same-origin via Firebase rewrite)
const res = await api.get(endpoints.Driver.getDriver);
ExportWhat it is
endpointsA map of endpoint paths grouped by feature, e.g. endpoints.Driver.getDriver'/Driver/getDriver'.
headersThe default request headers — { x_pvr_tech_studio_access: '123' }.
createApi(baseUrl)A tiny fetch wrapper returning { get, post }, with headers merged in automatically.

The base URL

createApi() defaults to a same-origin base of /api in the browser (the Firebase Hosting rewrite forwards /api to the Cloud Run service), and to http://localhost:8080/api when running on localhost:

export function defaultBaseUrl() {
  if (typeof window !== 'undefined' && window.location?.hostname === 'localhost') {
    return 'http://localhost:8080/api';
  }
  return '/api';
}

Pass your own base to override it: createApi('https://api.yourcompany.com').

The fetch wrapper

createApi is the React/Vue replacement for the HTML edition's scattered $.ajax calls. Every request merges in the auth header; non-2xx responses throw:

export function createApi(baseUrl = defaultBaseUrl()) {
  const request = async (endpoint, options = {}) => {
    const res = await fetch(baseUrl + endpoint, {
      ...options,
      headers: { ...headers, ...(options.headers || {}) },
    });
    if (!res.ok) throw new Error(`API ${endpoint} -> ${res.status}`);
    return res.json();
  };
  return {
    get:  (endpoint)       => request(endpoint, { method: 'GET' }),
    post: (endpoint, body) => request(endpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    }),
  };
}

The endpoint groups

endpoints covers 48 feature groups (≈77 endpoints). The surface is almost entirely GET-only — these are read-only mock fixtures. The only writes are on MailSettings.

AreaGroups
MastersRoleMaster, UserMaster, CustomerMaster, CustomerGroups, ZoneMaster, LocationMaster, FleetModels, VehicleMaster, ReasonsMaster, Tariff
Bookings & dispatchBookingPlanner (getAllot20/getAllot60/getAllotMore60, getDrop15/getDrop30/getDrop60), AssignAllot, AutoAssignedBookings, PickupDrop, FreeVehicle, LockVehicle
TripsRegularTrips, IntercityBookings, CancelledTrips, CCCollections
CRMEnquiries, FollowUp, FeedbackComplaint, DndList, CallFlow
Fleet, owners, driversVehicle, Owner, Driver, AttendanceBreak
PaymentsPaymentList, PendingPayment
ReportsBookingReport, CancelledBooking, VehicleSummary, AttendanceReport, CustomersReport, TopCustomers, DispatcherWise, VehicleConsolidate, VehicleStatistics (getLoggedIn/getLoggedOut/getStatistics), PNR
SettingsSiteSettings, PaymentSettings, AnyVehicleSettings, PermissionSettings, MailSettings
MiscEmployees

MailSettings is the only group with writes:

endpoints.MailSettings = {
  getMailSettings:  '/MailSettings/getMailSettings',   // GET
  saveMailSettings: '/MailSettings/saveMailSettings',  // POST
  sendTestEmail:    '/MailSettings/sendTestEmail',     // POST
};
 
await api.post(endpoints.MailSettings.saveMailSettings, payload);

The auth header

Every request carries a single fixed header — the demo access key:

x_pvr_tech_studio_access: 123

createApi adds it automatically from the exported headers. The mock API requires it only on writes; GET reads are open (and CDN-cached). Replace it with your real auth scheme (API key, bearer token) when you wire up a real backend.

The bundled mock API

services/api is an Express 4 app (Node 20) that returns static JSON fixtures. Each feature is a folder under src/routes / src/controllers; the controller wraps every fixture in the same envelope. For RoleMaster:

// RoleMaster.controller.js
exports.getRoleMaster = async (req, res, next) => {
  const formattedResponse = formatResponse({
    status:  'success',
    code:    appConfig.responseCode.successCode,  // 5000
    message: appConfig.customMessages.successMsg, // "Data fetched successfully"
    data:    roleMasterData.data,                 // the array the UI renders
  });
  res.status(200).json(formattedResponse);
};

Routes mount each feature under /taxi-crm/api/<Feature> inside the Express app (e.g. app.use("/taxi-crm/api/RoleMaster", routes.RoleMaster)); in production the Firebase Hosting rewrite forwards same-origin /api/** to that Cloud Run service. Start it locally with npm run dev:api; it listens on port 8080.

So the live request and response for the driver list look like this:

GET http://localhost:8080/api/Driver/getDriver
Headers: x_pvr_tech_studio_access: 123
{
  "status": "success",
  "code": 5000,
  "message": "Data fetched successfully",
  "data": [
    { "driverId": "D-1001", "name": "Ar…", "mobile": "98…", "vehicleNo": "TN…", "city": "…" }
  ]
}

The response envelope

Every endpoint returns the same four-field envelope (built by responseFormatter.js). Your backend should match it so the editions keep working unchanged:

FieldMeaning
status"success" or "failure".
codeA numeric application code: 5000 success, 5001 created, 5002 retrieved, 5005 no data, 5008 failure (see config.jsresponseCode).
messageHuman-readable message.
dataThe payload — for list pages, the array the table renders.

Errors take a different shape — the centralized handler returns { error: { status, message, timestamp, … } } with Cache-Control: no-store.

How an edition reads it

A React/Vue component reads a list straight off res.data:

const api = createApi();
api.get(endpoints.Driver.getDriver)
   .then((res) => setDrivers(res.data || []))
   .catch((e) => setError(e.message));

The HTML edition uses its own API_CONFIG object in assets/js/core/app.js rather than the ES-module contract. It has an API_CONFIG.staticMode switch:

  • staticMode: false (source/demo default) — getUrl(path) joins baseUrl + path (http://localhost:8080/taxi-crm/api locally, https://api.pvrtechstudio.com/taxi-crm/api in the seller's demo), the live-API equivalent of createApi.
  • staticMode: truegetUrl(path) returns a bundled assets/data/{Feature}/{action}.json path. The ThemeForest download ships this way (the build pre-renders the mock API's responses to JSON), so the HTML edition runs with no backend on any static server.

DataTables list pages pass getUrl(...) into the table's ajax option with dataSrc: "data". See Tables and JavaScript Helpers.

Recipe: point an edition at your own backend

  1. React / Vue: pass your base to createApi, e.g. createApi('https://api.yourcompany.com'), or rely on the same-origin /api and point the hosting rewrite at your server.
  2. HTML: in assets/js/core/app.js set API_CONFIG.staticMode = false (if you started from the static ThemeForest build) and point API_CONFIG.baseUrl at your server.
  3. Match the paths. Keep the /{Feature}/{action} paths from endpoints if you can — then nothing else changes. Otherwise edit the values to match your routes.
  4. Return the envelope. Make each endpoint respond with { status, code, message, data }, with the row array under data, and the field names each screen expects.
  5. Set headers / auth. Replace headers.x_pvr_tech_studio_access with your real auth header (API key, bearer token); createApi and API_CONFIG.headers send it on every call.

Note: translations are fetched too

Separately from the API, the i18n engine loads locale JSON from assets/locales/ (8 languages). Some browsers block fetch() on file:// URLs, so serve the folder over HTTP if text shows raw keys. Details in Internationalization.

Was this page helpful?