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