> ## Documentation Index
> Fetch the complete documentation index at: https://inertiajs-vi.tuantq.online/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP Requests

export const VueSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return "Vue";
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || "Vue";
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "Vue") {
    return null;
  }
  return children;
};

export const SvelteSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (!code?.includes("Svelte")) {
    return null;
  }
  return children;
};

export const ReactSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "React") {
    return null;
  }
  return children;
};

export const ClientSpecific = ({children}) => {
  const [nada, setNada] = useState();
  return children;
};

Không phải request nào cũng cần kích hoạt Inertia page visit. Với request tới API bên ngoài hoặc lấy dữ liệu từ endpoint không phải Inertia, hook `useHttp` cung cấp trải nghiệm phát triển tương tự `useForm` nhưng dành cho HTTP request độc lập.

## Cách dùng cơ bản

Hook `useHttp` nhận dữ liệu ban đầu và trả về reactive state cùng các phương thức gửi HTTP request.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useHttp } from '@inertiajs/vue3'

  const http = useHttp({
      query: '',
  })

  function search() {
      http.get('/api/search', {
          onSuccess: (response) => {
              console.log(response)
          },
      })
  }
  </script>

  <template>
      <input v-model="http.query" @input="search" />
      <div v-if="http.processing">Searching...</div>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useHttp } from '@inertiajs/react'

  export default function Search() {
      const { data, setData, get, processing } = useHttp({
          query: '',
      })

      function search(e) {
          setData('query', e.target.value)
          get('/api/search', {
              onSuccess: (response) => {
                  console.log(response)
              },
          })
      }

      return (
          <>
              <input value={data.query} onChange={search} />
              {processing && <div>Searching...</div>}
          </>
      )
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { useHttp } from '@inertiajs/svelte'

  const http = useHttp({
      query: '',
  })

  function search() {
      http.get('/api/search', {
          onSuccess: (response) => {
              console.log(response)
          },
      })
  }
  </script>

  <input bind:value={http.query} oninput={search} />
  {#if http.processing}
      <div>Searching...</div>
  {/if}
  ```
</CodeGroup>

Khác router visit, request `useHttp` không kích hoạt page navigation hay tương tác với page lifecycle của Inertia. Đây là HTTP request thông thường trả JSON response.

## Gửi dữ liệu

Hook cung cấp các phương thức tiện ích `get`, `post`, `put`, `patch` và `delete`. Ngoài ra còn có phương thức `submit` tổng quát cho HTTP method động.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  http.get(url, options)
  http.post(url, options)
  http.put(url, options)
  http.patch(url, options)
  http.delete(url, options)
  http.submit(method, url, options)
  ```

  ```js React icon="react" theme={null}
  const { get, post, put, patch, delete: destroy, submit } = useHttp({ ... })

  get(url, options)
  post(url, options)
  put(url, options)
  patch(url, options)
  destroy(url, options)
  submit(method, url, options)
  ```

  ```js Svelte icon="s" theme={null}
  http.get(url, options)
  http.post(url, options)
  http.put(url, options)
  http.patch(url, options)
  http.delete(url, options)
  http.submit(method, url, options)
  ```
</CodeGroup>

Mỗi phương thức trả một `Promise` resolve với dữ liệu JSON response đã parse. Người dùng TypeScript có thể [khai báo type cho request data và response](/v3/advanced/typescript#http-helper).

### Bind endpoint trước

Bạn có thể truyền HTTP method và URL làm hai đối số đầu của `useHttp()`, sau đó gọi `submit()` không cần đối số để gửi request. Cách này cũng mở khóa validation thời gian thực. Xem [Precognition](#precognition) để biết chi tiết.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const response = await http.post('/api/comments', {
      onError: (errors) => {
          console.log(errors)
      },
  })
  ```

  ```js React icon="react" theme={null}
  const response = await post('/api/comments', {
      onError: (errors) => {
          console.log(errors)
      },
  })
  ```

  ```js Svelte icon="s" theme={null}
  const response = await http.post('/api/comments', {
      onError: (errors) => {
          console.log(errors)
      },
  })
  ```
</CodeGroup>

## Nhiều request

Mỗi instance `useHttp` theo dõi `processing`, `errors` và reactive state riêng. Khi gửi các request độc lập, bạn có thể tạo instance riêng cho từng request để state không xung đột.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const search = useHttp({ query: '' })
  const upload = useHttp({ file: null })
  ```

  ```js React icon="react" theme={null}
  const search = useHttp({ query: '' })
  const upload = useHttp({ file: null })
  ```

  ```js Svelte icon="s" theme={null}
  const search = useHttp({ query: '' })
  const upload = useHttp({ file: null })
  ```
</CodeGroup>

## Reactive state

Hook `useHttp` cung cấp các reactive property tương tự `useForm`:

| Thuộc tính           | Kiểu             | Mô tả                                            |
| -------------------- | ---------------- | ------------------------------------------------ |
| `errors`             | `object`         | Validation error theo key là tên field           |
| `hasErrors`          | `boolean`        | Có tồn tại validation error hay không            |
| `processing`         | `boolean`        | Có request đang được xử lý hay không             |
| `progress`           | `object \| null` | Tiến trình upload với `percentage` và `total`    |
| `wasSuccessful`      | `boolean`        | Request gần nhất có thành công hay không         |
| `recentlySuccessful` | `boolean`        | `true` trong hai giây sau một request thành công |
| `isDirty`            | `boolean`        | Dữ liệu có khác giá trị mặc định hay không       |

## Lỗi validation

Khi request trả status code `422`, hook tự động parse validation error và cung cấp chúng qua thuộc tính `errors`.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useHttp } from '@inertiajs/vue3'

  const http = useHttp({
      name: '',
      email: '',
  })

  function save() {
      http.post('/api/users')
  }
  </script>

  <template>
      <input v-model="http.name" />
      <div v-if="http.errors.name">{{ http.errors.name }}</div>

      <input v-model="http.email" />
      <div v-if="http.errors.email">{{ http.errors.email }}</div>

      <button @click="save" :disabled="http.processing">Save</button>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useHttp } from '@inertiajs/react'

  export default function CreateUser() {
      const { data, setData, post, errors, processing } = useHttp({
          name: '',
          email: '',
      })

      function save(e) {
          e.preventDefault()
          post('/api/users')
      }

      return (
          <form onSubmit={save}>
              <input value={data.name} onChange={e => setData('name', e.target.value)} />
              {errors.name && <div>{errors.name}</div>}

              <input value={data.email} onChange={e => setData('email', e.target.value)} />
              {errors.email && <div>{errors.email}</div>}

              <button type="submit" disabled={processing}>Save</button>
          </form>
      )
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { useHttp } from '@inertiajs/svelte'

  const http = useHttp({
      name: '',
      email: '',
  })

  function save() {
      http.post('/api/users')
  }
  </script>

  <input bind:value={http.name} />
  {#if http.errors.name}
      <div>{http.errors.name}</div>
  {/if}

  <input bind:value={http.email} />
  {#if http.errors.email}
      <div>{http.errors.email}</div>
  {/if}

  <button onclick={save} disabled={http.processing}>Save</button>
  ```
</CodeGroup>

## Hiển thị tất cả lỗi

Mặc định, validation error được rút gọn còn message đầu tiên cho mỗi field. Bạn có thể nối `withAllErrors()` để nhận toàn bộ error message dưới dạng mảng, hữu ích với field có nhiều validation rule.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const http = useHttp({
      name: '',
      email: '',
  }).withAllErrors()

  // http.errors.name === ['Name is required.', 'Name must be at least 3 characters.']
  ```

  ```js React icon="react" theme={null}
  const http = useHttp({
      name: '',
      email: '',
  }).withAllErrors()

  // http.errors.name === ['Name is required.', 'Name must be at least 3 characters.']
  ```

  ```js Svelte icon="s" theme={null}
  const http = useHttp({
      name: '',
      email: '',
  }).withAllErrors()

  // http.errors.name === ['Name is required.', 'Name must be at least 3 characters.']
  ```
</CodeGroup>

Phương thức tương tự cũng có trên helper [`useForm`](/v3/the-basics/forms#options-2) và component [`<Form>`](/v3/the-basics/forms#options).

## Tải file lên

Khi dữ liệu có file, hook tự động gửi request dưới dạng `multipart/form-data`. Tiến trình upload có sẵn qua thuộc tính `progress`.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useHttp } from '@inertiajs/vue3'

  const http = useHttp({
      file: null,
  })

  function upload() {
      http.post('/api/uploads')
  }
  </script>

  <template>
      <input type="file" @change="http.file = $event.target.files[0]" />
      <progress v-if="http.progress" :value="http.progress.percentage" max="100" />
      <button @click="upload" :disabled="http.processing">Upload</button>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useHttp } from '@inertiajs/react'

  export default function Upload() {
      const { setData, post, progress, processing } = useHttp({
          file: null,
      })

      return (
          <>
              <input type="file" onChange={e => setData('file', e.target.files[0])} />
              {progress && <progress value={progress.percentage} max="100" />}
              <button onClick={() => post('/api/uploads')} disabled={processing}>Upload</button>
          </>
      )
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { useHttp } from '@inertiajs/svelte'

  const http = useHttp({
      file: null,
  })

  function upload() {
      http.post('/api/uploads')
  }
  </script>

  <input type="file" onchange={e => http.file = e.target.files[0]} />
  {#if http.progress}
      <progress value={http.progress.percentage} max="100" />
  {/if}
  <button onclick={upload} disabled={http.processing}>Upload</button>
  ```
</CodeGroup>

## Hủy request

Bạn có thể hủy request đang chạy bằng phương thức `cancel()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  http.cancel()
  ```

  ```js React icon="react" theme={null}
  const { cancel } = useHttp({ ... })

  cancel()
  ```

  ```js Svelte icon="s" theme={null}
  http.cancel()
  ```
</CodeGroup>

## Optimistic updates

Hook `useHttp` hỗ trợ [optimistic update](/v3/the-basics/optimistic-updates) qua phương thức `optimistic()`. Callback nhận dữ liệu hiện tại và nên trả về partial update để áp dụng ngay.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  http.optimistic((data) => ({
      likes: data.likes + 1,
  })).post('/api/likes')
  ```

  ```js React icon="react" theme={null}
  const { optimistic, post } = useHttp({ likes: 0 })

  optimistic((data) => ({
      likes: data.likes + 1,
  }))
  post('/api/likes')
  ```

  ```js Svelte icon="s" theme={null}
  http.optimistic((data) => ({
      likes: data.likes + 1,
  })).post('/api/likes')
  ```
</CodeGroup>

Update được áp dụng đồng bộ. Nếu request thất bại, dữ liệu được rollback về state trước đó.

## Callback sự kiện

Mỗi phương thức submit nhận options object với các lifecycle callback:

```js theme={null}
http.post('/api/users', {
    onBefore: () => { ... },
    onStart: () => { ... },
    onProgress: (progress) => { ... },
    onSuccess: (data, response) => { ... },
    onError: (errors) => { ... },
    onHttpException: (response) => { ... },
    onNetworkError: (error) => { ... },
    onCancel: () => { ... },
    onFinish: () => { ... },
})
```

Bạn có thể trả `false` từ `onBefore` để hủy request.

### Truy cập HTTP response

Callback `onSuccess` nhận dữ liệu response đã parse làm đối số đầu và HTTP response object làm đối số thứ hai. Response object chứa `status`, `data` và `headers`, hữu ích khi cần kiểm tra status code hoặc response header.

```js theme={null}
http.post('/api/users', {
    onSuccess: (data, response) => {
        console.log(response.status) // 200, 201, etc.
    },
})
```

### HTTP exception

Callback `onHttpException` chạy khi server trả HTTP error không phải 422, chẳng hạn `500` hoặc `403`. Callback nhận HTTP response object, cho phép truy cập status code, response body và header.

```js theme={null}
http.post('/api/users', {
    onHttpException: (response) => {
        console.log(response.status) // 500, 403, etc.
        console.log(response.data)   // Response body
    },
})
```

### Network error

Callback `onNetworkError` chạy khi request thất bại do vấn đề mạng, chẳng hạn người dùng mất kết nối Internet. Callback nhận object `Error`.

```js theme={null}
http.post('/api/users', {
    onNetworkError: (error) => {
        console.log(error.message)
    },
})
```

## Precognition

Hook `useHttp` hỗ trợ [Laravel Precognition](https://laravel.com/docs/precognition) cho validation thời gian thực. Bật tính năng bằng cách nối `withPrecognition()` với HTTP method và validation endpoint.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useHttp } from '@inertiajs/vue3'

  const http = useHttp({
      name: '',
      email: '',
  }).withPrecognition('post', '/api/users')
  ```

  ```js React icon="react" theme={null}
  import { useHttp } from '@inertiajs/react'

  const http = useHttp({
      name: '',
      email: '',
  }).withPrecognition('post', '/api/users')
  ```

  ```js Svelte icon="s" theme={null}
  import { useHttp } from '@inertiajs/svelte'

  const http = useHttp({
      name: '',
      email: '',
  }).withPrecognition('post', '/api/users')
  ```
</CodeGroup>

Sau khi bật, các phương thức `validate()`, `touch()`, `touched()`, `valid()` và `invalid()` trở nên khả dụng và hoạt động giống [form precognition](/v3/the-basics/forms#precognition-1).

## History state

Bạn có thể lưu dữ liệu và lỗi vào browser history state bằng cách truyền remember key làm đối số đầu tiên.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const http = useHttp('SearchData', {
      query: '',
  })
  ```

  ```js React icon="react" theme={null}
  const http = useHttp('SearchData', {
      query: '',
  })
  ```

  ```js Svelte icon="s" theme={null}
  const http = useHttp('SearchData', {
      query: '',
  })
  ```
</CodeGroup>

Bạn có thể loại các field nhạy cảm khỏi history state bằng phương thức `dontRemember()`.

```js theme={null}
const http = useHttp('Login', {
    email: '',
    token: '',
}).dontRemember('token')
```

***

## Tài liệu chính thức

Bài dịch này được đối chiếu từ [tài liệu Inertia.js v3 chính thức](https://inertiajs.com/docs/v3/the-basics/http-requests). Nếu có khác biệt do phiên bản hoặc cập nhật mới, hãy ưu tiên tài liệu chính thức làm nguồn tham chiếu.
