Skip to main content

Two HTTP patterns

The client uses two different HTTP approaches — both coexist in the codebase:

1. Axios (configured in main.js)

// Available as this.$http in all components
const base = axios.create({
  baseURL: process.env.VUE_APP_API_URL,  // e.g., "https://tutorbloc.com/api"
  auth: {
    username: process.env.VUE_APP_API_USERNAME,
    password: process.env.VUE_APP_API_PASSWORD
  }
});
Vue.prototype.$http = base;
Used in some components but not the primary pattern.

2. XMLHttpRequest (Promise-wrapped)

The dominant pattern across the codebase:
var request = new XMLHttpRequest();
request.responseType = 'json';
request.withCredentials = true;

return new Promise((resolve, reject) => {
  request.onreadystatechange = function() {
    if (request.readyState !== 4) return;
    if (request.status >= 200 && request.status < 300) {
      resolve(request);
    } else {
      reject(request);
    }
  };
  request.open("GET", `${apiBaseUrl}/endpoint`);
  request.setRequestHeader("Content-Type", "application/json");
  request.setRequestHeader("Accept", "application/json");
  request.setRequestHeader("sessionKey", this.session_key);
  request.send();
});
There’s no centralized API service layer. Each component defines its own API calls inline in its methods block. The apiFunctions.js file has a few shared helpers but most calls are component-local.

Authentication

For the backend implementation of the auth system (custom Redis token guard), see Authentication architecture.

How it works

  1. User logs in via POST /auth/login (or registers via POST /auth/register)
  2. Backend returns a session_key token
  3. Client stores it in component state (passed via props) or sessionStorage
  4. Subsequent requests include sessionKey header
request.setRequestHeader("sessionKey", this.session_key);
// OR
request.setRequestHeader("sessionKey", this.user.account.session_key);

Storage

  • Component statesession_key passed through props between components
  • sessionStorage — used in Dashboard (sessionStorage.setItem('session_key', ...))
  • No localStorage — session doesn’t persist across browser close
  • No refresh token mechanism — if the token expires, user must re-login
There’s no global auth store or interceptor. If a request returns 401, handling is component-specific. There’s no automatic redirect to login on auth failure.

API helper functions

File: src/assets/scripts/apiFunctions.js
FunctionEndpointPurpose
getTutorDetails(id)GET /tutors/{id}/profileFetch public tutor profile
sendCode(number, code)GET /mobile-numbers/codeSend SMS verification code
getAddresses(postcode)GET /addresses/searchSearch UK addresses by postcode
getAddressesWithCountry(postcode, code)GET /addresses/searchAddress search with country filter
getActionEmail(actionID, lesson)Extract email from lesson data
getActionName(actionID, lesson)Extract name from lesson data
getActionFullName(actionID, lesson)Extract full name from lesson data

Complete API endpoint map

Every API endpoint the client calls, mapped to the backend. For full backend endpoint documentation, see the API Reference.

Authentication (backend docs)

Client actionMethodEndpoint
LoginPOST/auth/login
Register tutorPOST/auth/register
Forgot passwordPOST/auth/forgot
Dashboard loginPOST/v1/auth/login
Resend verificationGET/v1/auth/email/verify/resend

Users & profiles (backend docs)

Client actionMethodEndpoint
Validate emailGET/users/{email}/validate
Validate usernameGET/users/{username}/validate
Get user profileGET/users/{id}
Get tutor profileGET/tutors/{id}/profile
Search tutor by emailGET/tutors?email={email}
Get self (dashboard)GET/v1/users/self

Booking & lessons (backend docs)

Client actionMethodEndpoint
Get availabilityGET/availabilites?duration=&lesson_type=&teach_id=&dates[]=
Get lesson locationsGET/lesson-locations?tutor_id={id}
Get lesson priceGET/lesson-price?teach_id=&duration=
Get price with couponGET/lesson-price?teach_id=&duration=&coupon_uid=
Create bookingPOST/bookings
Get bookingGET/bookings/{id}?secret={secret}
Get own bookingsGET/bookings/self?limit=1
Update bookingPUT/bookings/{id}?secret={secret}
Cancel bookingPUT/bookings/{id}/cancel?secret={secret}

Payments (backend docs)

Client actionMethodEndpoint
Process paymentPOST/payments/pay
Check payment statusGET/payments/{secret}/status
Add cardPOST/cards
Validate cardPOST/cards/validate
List cardsGET/cards
Validate couponGET/coupons/{code}/validate

Other

Client actionMethodEndpoint
Get teachesGET/teaches?tutor_id={id}
Get countriesGET/countries
Upload filePOST/files
Get childrenGET/children
Add childPOST/children
Create addressPOST/addresses
Search addressesGET/addresses/search?post_code={postcode}
Send SMS codeGET/mobile-numbers/code?mobile_number={number}
Verify SMS codeGET/mobile-numbers/verify?mobile_number={number}&code={code}
SubscribePOST/subscriptions

Data structures

No TypeScript — these are the implicit shapes used across the codebase:

User object

{
  id: number,
  firstName: string,
  lastName: string,
  email: string,
  role_type: number,          // 1=tutor, 2=student, 3=parent
  session_key: string,
  teaches: [{
    id: number,
    subject: { title: string },
    subject_level: { title: string },
    exam_boards: [{ id, title }],
    hourly_rate: number
  }],
  profile_picture_url: string,
  bio: string,
  education: { university, course, grade, start_date, end_date },
  languages: [string],
  rating: { average_rating: number, number_of_reviews: number },
  tutoring_experience: number,  // months
  video_url: string,
  current_location: string
}

Registration payload

{
  username, email, password,
  firstname, lastname,
  role_type: 1,               // Always tutor for registration
  country_id, dob,
  payment_card: { number, exp_month, exp_year, cvc },
  mobile_number: { country_code, mobile_number },
  dbs: { certificate_number, date_of_issue, file_id },
  address: { line_1, line_2, line_3, post_code, country_name, lat, lng }
}

Booking payload

{
  tutor_id, teach_id,
  location: string,           // "online" | "tutor" | "student"
  duration: number,           // minutes (15-120)
  date: string,               // YYYY-MM-DD
  start: string, end: string, // HH:MM
  message: string,
  // For in-person:
  number, street, town, postcode, lat, lng,
  // Optional:
  file: { id, content }
}

Error handling

No global error interceptor. Each component handles errors locally:
this.apiCall()
  .then(response => { /* handle success */ })
  .catch(err => {
    this.error = err.response?.title || 'Something went wrong';
    this.validInput = false;
  });
Specific error handling examples:
  • Login: 404 = user not found, 401 = invalid credentials
  • Payment: insufficient funds, 3D Secure failures with specific messages
  • Registration: duplicate email/username with API error text display