2 * Perform a HTTP GET request.
3 * Can easily pass query parameters as the second parameter.
5 * @param {Object} params
6 * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
8 async function get(url, params = {}) {
16 * Perform a HTTP POST request.
18 * @param {Object} data
19 * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
21 async function post(url, data = null) {
22 return dataRequest('POST', url, data);
26 * Perform a HTTP PUT request.
28 * @param {Object} data
29 * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
31 async function put(url, data = null) {
32 return dataRequest('PUT', url, data);
36 * Perform a HTTP PATCH request.
38 * @param {Object} data
39 * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
41 async function patch(url, data = null) {
42 return dataRequest('PATCH', url, data);
46 * Perform a HTTP DELETE request.
48 * @param {Object} data
49 * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
51 async function performDelete(url, data = null) {
52 return dataRequest('DELETE', url, data);
56 * Perform a HTTP request to the back-end that includes data in the body.
57 * Parses the body to JSON if an object, setting the correct headers.
58 * @param {String} method
60 * @param {Object} data
61 * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
63 async function dataRequest(method, url, data = null) {
69 // Send data as JSON if a plain object
70 if (typeof data === 'object' && !(data instanceof FormData)) {
72 'Content-Type': 'application/json',
73 'X-Requested-With': 'XMLHttpRequest',
75 options.body = JSON.stringify(data);
78 // Ensure FormData instances are sent over POST
79 // Since Laravel does not read multipart/form-data from other types
80 // of request. Hence the addition of the magic _method value.
81 if (data instanceof FormData && method !== 'post') {
82 data.append('_method', method);
83 options.method = 'post';
86 return request(url, options);
90 * Create a new HTTP request, setting the required CSRF information
91 * to communicate with the back-end. Parses & formats the response.
93 * @param {Object} options
94 * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
96 async function request(url, options = {}) {
97 if (!url.startsWith('http')) {
98 url = window.baseUrl(url);
101 if (options.params) {
102 const urlObj = new URL(url);
103 for (const paramName of Object.keys(options.params)) {
104 const value = options.params[paramName];
105 if (typeof value !== 'undefined' && value !== null) {
106 urlObj.searchParams.set(paramName, value);
109 url = urlObj.toString();
112 const csrfToken = document.querySelector('meta[name=token]').getAttribute('content');
113 options = {...options, credentials: 'same-origin'};
115 ...options.headers || {},
116 baseURL: window.baseUrl(''),
117 'X-CSRF-TOKEN': csrfToken,
120 const response = await fetch(url, options);
121 const content = await getResponseContent(response);
124 headers: response.headers,
125 redirected: response.redirected,
126 status: response.status,
127 statusText: response.statusText,
133 throw new HttpError(response, content);
140 * Get the content from a fetch response.
141 * Checks the content-type header to determine the format.
142 * @param {Response} response
143 * @returns {Promise<Object|String>}
145 async function getResponseContent(response) {
146 if (response.status === 204) {
150 const responseContentType = response.headers.get('Content-Type') || '';
151 const subType = responseContentType.split(';')[0].split('/').pop();
153 if (subType === 'javascript' || subType === 'json') {
154 return await response.json();
157 return await response.text();
160 class HttpError extends Error {
162 constructor(response, content) {
163 super(response.statusText);
165 this.headers = response.headers;
166 this.redirected = response.redirected;
167 this.status = response.status;
168 this.statusText = response.statusText;
169 this.url = response.url;
170 this.original = response;
180 delete: performDelete,