Skip to content

Server Error Integration ​

15.3.1. Backend Error Response Format ​

The TQS standard backend error response follows the structure below. The frontend must handle errors according to this format.

typescript
// src/types/api.ts
interface ApiErrorResponse {
  errorCode: string        // Error code (e.g., 'VALIDATION_ERROR', 'CONFLICT')
  message: string          // User-facing message
  fieldErrors?: {          // Per-field validation errors (optional)
    field: string
    message: string
  }[]
}
FieldRequiredDescription
errorCodeRequiredError classification code
messageRequiredGlobal error message
fieldErrorsOptionalList of field-level validation failures
  • fieldErrors is included only when server-side validation fails during form submission.
  • errorCode is used by the frontend for branching logic based on error type.

15.3.2. Per-Field Error Mapping ​

fieldErrors returned from the server must be mapped to the form's errors state.

typescript
// src/utils/mapServerErrors.ts
import type { ApiErrorResponse } from '@/types/api'

export function mapFieldErrors(
  response: ApiErrorResponse
): Record<string, string> {
  const errors: Record<string, string> = {}

  if (response.fieldErrors) {
    for (const fieldError of response.fieldErrors) {
      errors[fieldError.field] = fieldError.message
    }
  }

  return errors
}

This is applied in the form submission handler as follows.

typescript
import { mapFieldErrors } from '@/utils/mapServerErrors'
import axios from 'axios'

async function handleSubmit() {
  isSubmitting.value = true
  errors.value = {}

  try {
    await createUser(form.value)
  } catch (e) {
    if (axios.isAxiosError(e) && e.response?.data) {
      const serverError = e.response.data as ApiErrorResponse
      errors.value = mapFieldErrors(serverError)
    }
  } finally {
    isSubmitting.value = false
  }
}
  • The server's field values must match the keys of the form object.
  • Field naming conventions must be agreed upon between the backend and frontend in advance.

15.3.3. Global Error Handling ​

Errors that are not mapped to fieldErrors must be treated as global errors. They must be displayed as toast notifications or banners at the top of the form.

typescript
import { useToast } from '@/composables/useToast'

const { showToast } = useToast()

async function handleSubmit() {
  isSubmitting.value = true
  errors.value = {}

  try {
    await createUser(form.value)
  } catch (e) {
    if (axios.isAxiosError(e) && e.response?.data) {
      const serverError = e.response.data as ApiErrorResponse

      const fieldErrors = mapFieldErrors(serverError)
      if (Object.keys(fieldErrors).length > 0) {
        errors.value = fieldErrors
      } else {
        showToast({ type: 'error', message: serverError.message })
      }
    } else {
      showToast({ type: 'error', message: 'An error occurred while processing the request.' })
    }
  } finally {
    isSubmitting.value = false
  }
}
Error TypeDisplay MethodExample
Field validation errorInline below field"This email is already in use."
Business logic errorToast notification"Daily registration limit exceeded."
Network/server errorToast notification"An error occurred while processing the request."

15.3.4. Error Reset ​

When the user modifies a form, the related errors must be automatically cleared. All errors must be cleared upon resubmission.

typescript
import { watch } from 'vue'

// Clear the corresponding error when an individual field changes
watch(
  () => form.value.email,
  () => {
    if (errors.value.email) {
      delete errors.value.email
    }
  }
)

// General reset — watch the entire form
watch(
  form,
  () => {
    errors.value = {}
  },
  { deep: true }
)
  • Field-level clearing removes only the specific field's error, preserving error messages on other fields.
  • Watching the entire form clears all errors at once, which is concise but should be noted that errors on other fields will also disappear.
  • The appropriate strategy should be selected based on project requirements.

TIENIPIA QUALIFIED STANDARD