DEV Community

Software Developer
Software Developer

Posted on

100+ AJAX / fetch / HTTP code snippets

Table of Contents (by category)

  1. Vanilla JavaScript / Fetch / XMLHttpRequest
  2. jQuery AJAX patterns
  3. Axios / modern libraries
  4. Utilities & helpers
  5. Advanced / composite patterns
  6. Error handling, retries, timeouts
  7. Miscellaneous tricks
  8. Minimal examples ready to drop in

1. Vanilla JavaScript / Fetch / XMLHttpRequest

// 1. Basic fetch GET (JSON)
fetch('/api/data')
  .then(res => {
    if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
    return res.json();
  })
  .then(data => console.log(data))
  .catch(err => console.error('Fetch error:', err));

// 2. Fetch POST with JSON body
fetch('/api/submit', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Alice', age: 30 })
})
  .then(res => res.json())
  .then(data => console.log('Response:', data))
  .catch(err => console.error('Error:', err));

// 3. Fetch with query parameters helper
function fetchWithParams(url, params) {
  const query = new URLSearchParams(params).toString();
  return fetch(`${url}?${query}`);
}
// usage:
fetchWithParams('/api/list', { page: 2, per_page: 10 })
  .then(res => res.json())
  .then(data => console.log(data));

// 4. Fetch with timeout (using AbortController)
function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  options.signal = controller.signal;
  const timer = setTimeout(() => controller.abort(), timeout);
  return fetch(url, options)
    .finally(() => clearTimeout(timer));
}
// usage:
fetchWithTimeout('/api/slow', {}, 3000)
  .then(res => res.json())
  .catch(err => {
    if (err.name === 'AbortError') console.error('Request timed out');
    else console.error(err);
  });

// 5. Fetch and stream (reading large responses chunk by chunk)
async function fetchStream(url) {
  const res = await fetch(url);
  const reader = res.body.getReader();
  let done = false;
  while (!done) {
    const { value, done: d } = await reader.read();
    done = d;
    if (value) {
      // value is a Uint8Array chunk
      console.log('Chunk length:', value.length);
      // process chunk
    }
  }
}
// usage
fetchStream('/api/largefile');

// 6. XMLHttpRequest basic GET
function xhrGet(url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.onload = () => {
    if (xhr.status >= 200 && xhr.status < 300) callback(null, xhr.responseText);
    else callback(new Error(`Status: ${xhr.status}`));
  };
  xhr.onerror = () => callback(new Error('Network error'));
  xhr.send();
}
// usage
xhrGet('/api/hello', (err, text) => {
  if (err) console.error(err);
  else console.log(text);
});

// 7. XMLHttpRequest POST (form URL-encoded)
function xhrPost(url, dataObj, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('POST', url, true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.onload = () => {
    if (xhr.status >= 200 && xhr.status < 300) callback(null, xhr.responseText);
    else callback(new Error(`Status: ${xhr.status}`));
  };
  xhr.onerror = () => callback(new Error('Network error'));
  const body = Object.entries(dataObj).map(
    ([k,v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`
  ).join('&');
  xhr.send(body);
}
// usage
xhrPost('/api/save', { name: 'Bob', city: 'NY' }, (err, res) => {
  if (err) console.error(err);
  else console.log(res);
});

// 8. Polling (repeat request every interval)
function poll(url, interval = 5000, onData, onError) {
  async function doPoll() {
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error(`Bad status ${res.status}`);
      const data = await res.json();
      onData(data);
    } catch(err) {
      if (onError) onError(err);
    } finally {
      setTimeout(doPoll, interval);
    }
  }
  doPoll();
}
// usage
poll('/api/status', 3000, data => {
  console.log('Status update:', data);
}, err => {
  console.warn('Polling error:', err);
});

// 9. Debounced fetch (delay until user stops typing)
function debounce(fn, wait = 300) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), wait);
  };
}
const debouncedSearch = debounce(async (term) => {
  const res = await fetch(`/api/search?q=${encodeURIComponent(term)}`);
  const results = await res.json();
  console.log(results);
}, 500);
// usage: call debouncedSearch(inputValue) on input event

// 10. Combine multiple fetches (in parallel) & aggregate results
Promise.all([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/orders').then(r => r.json())
])
  .then(([users, orders]) => {
    console.log('Users:', users);
    console.log('Orders:', orders);
  })
  .catch(err => console.error('Error in one of the fetches:', err));

// 11. Sequential fetch chaining
fetch('/api/start')
  .then(r => r.json())
  .then(data => {
    return fetch(`/api/next?from=${data.next}`);
  })
  .then(r => r.json())
  .then(data2 => {
    console.log('Chained result:', data2);
  })
  .catch(err => console.error(err));

// 12. Fetch with retry logic
async function fetchWithRetry(url, options = {}, retries = 3, delayMs = 1000) {
  for (let i = 0; i < retries; i++) {
    try {
      const res = await fetch(url, options);
      if (!res.ok) throw new Error(`Bad status ${res.status}`);
      return res;
    } catch (err) {
      if (i === retries - 1) throw err;
      await new Promise(r => setTimeout(r, delayMs));
    }
  }
}
// usage
fetchWithRetry('/api/flaky', {}, 5, 500)
  .then(r => r.json())
  .then(data => console.log(data))
  .catch(err => console.error('Final failure:', err));

// 13. Fetch POST form data (multipart/form-data)
async function postFormData(url, formElement) {
  const formData = new FormData(formElement);
  const res = await fetch(url, {
    method: 'POST',
    body: formData
  });
  return res.json();
}
// usage: postFormData('/api/upload', document.querySelector('#myform')).then(...)

// 14. Abortable fetch (cancel request)
const controller = new AbortController();
fetch('/api/long', { signal: controller.signal })
  .then(r => r.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') console.log('Fetch aborted');
    else console.error(err);
  });
// to cancel:
controller.abort();

// 15. Using fetch for text / HTML (instead of JSON)
fetch('/api/page.html')
  .then(res => res.text())
  .then(html => {
    document.getElementById('container').innerHTML = html;
  });

// 16. Fetch HEAD request (check metadata)
fetch('/api/photo.jpg', { method: 'HEAD' })
  .then(res => {
    console.log('Content-Length:', res.headers.get('Content-Length'));
  });

// 17. Fetch JSON only if modified (using ETag / If-None-Match)
async function fetchIfModified(url, lastEtag = null) {
  const headers = {};
  if (lastEtag) headers['If-None-Match'] = lastEtag;
  const res = await fetch(url, { headers });
  if (res.status === 304) return null;  // not modified
  const newEtag = res.headers.get('ETag');
  const data = await res.json();
  return { data, etag: newEtag };
}
// usage: store etag and skip re-render if null

// 18. Long polling (repeat only after previous completes)
async function longPoll(url, onData, onError) {
  while (true) {
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error(`Status ${res.status}`);
      const data = await res.json();
      onData(data);
    } catch (err) {
      if (onError) onError(err);
      await new Promise(r => setTimeout(r, 1000));
    }
  }
}
// usage
// longPoll('/api/updates', data => console.log('update:', data));

// 19. Conditional fetch (cache-first)
const cache = new Map();
async function fetchCacheFirst(url) {
  if (cache.has(url)) {
    return cache.get(url);
  }
  const res = await fetch(url);
  const data = await res.json();
  cache.set(url, data);
  return data;
}
// usage fetchCacheFirst('/api/user').then(...)

// 20. Streaming upload (for big files)
async function uploadChunked(url, file) {
  const chunkSize = 1024 * 512; // 512 KB
  for (let start = 0; start < file.size; start += chunkSize) {
    const chunk = file.slice(start, start + chunkSize);
    const form = new FormData();
    form.append('chunk', chunk);
    form.append('start', start);
    await fetch(url, { method: 'POST', body: form });
  }
}
// usage: uploadChunked('/api/upload-chunk', fileInput.files[0]);
Enter fullscreen mode Exit fullscreen mode

2. jQuery AJAX patterns

(These snippets are classic and very widely used. Inspired by existing gist / references.)

// 21. Basic $.ajax POST (JSON)
$.ajax({
  url: '/api/endpoint',
  method: 'POST',
  contentType: 'application/json',
  data: JSON.stringify({ foo: 'bar' }),
  dataType: 'json',
  success: function(res) {
    console.log('Success:', res);
  },
  error: function(jqXHR, status, err) {
    console.error('AJAX error:', status, err);
  }
});

// 22. $.ajax GET (with query params)
$.ajax({
  url: '/api/users',
  method: 'GET',
  data: { page: 2, per_page: 20 },
  dataType: 'json',
  success: function(res) {
    console.log(res);
  },
  error: function(xhr) {
    console.error('Error:', xhr.status);
  }
});

// 23. Shortcut $.get / $.post
$.get('/api/status', { id: 5 }, function(res) {
  console.log(res);
});
$.post('/api/login', { user: 'alice', pass: '1234' })
  .done(res => console.log(res))
  .fail(err => console.error('Login failed:', err));

// 24. $.getJSON (expects JSON)
$.getJSON('/api/data', { q: 'search' }, function(res) {
  console.log('JSON:', res);
});

// 25. Load HTML into container
$('#container').load('/partial.html', function(response, status, xhr) {
  if (status === 'error') {
    console.error('Load error:', xhr.status, xhr.statusText);
  }
});

// 26. Before send / progress / completion
$.ajax({
  url: '/api/do',
  method: 'POST',
  data: { x: 1 },
  beforeSend: function(jqXHR, settings) {
    $('#spinner').show();
  },
  success: function(res) { console.log(res); },
  error: function(err) { console.error(err); },
  complete: function() {
    $('#spinner').hide();
  }
});

// 27. Global AJAX handlers (start, stop)
$(document).ajaxStart(() => {
  $('#globalLoader').show();
});
$(document).ajaxStop(() => {
  $('#globalLoader').hide();
});

// 28. Using this.custom property (passing extra data)
$.ajax({
  url: '/api/list',
  method: 'GET',
  myCustomIndex: 5,
  success: function(data) {
    console.log('Index was', this.myCustomIndex);
  }
});

// 29. Cache JSONP / cross-domain
$.ajax({
  url: 'https://another.com/api?callback=?',
  dataType: 'jsonp',
  success: function(data) {
    console.log('Cross-domain JSONP data:', data);
  }
});

// 30. Abort a jQuery AJAX
const req = $.ajax({
  url: '/api/long',
  method: 'GET'
});
// later
req.abort();

// 31. Multiple AJAX with $.when / $.Deferred
$.when(
  $.ajax('/api/a').done(a => console.log('A done', a)),
  $.ajax('/api/b').done(b => console.log('B done', b))
).then(function(aRes, bRes) {
  console.log('Both done', aRes, bRes);
}).fail(function() {
  console.error('One failed');
});

// 32. Polling with jQuery AJAX
function jqPoll(url, interval, onSuccess, onError) {
  function go() {
    $.ajax({
      url,
      method: 'GET',
      success: function(data) {
        onSuccess(data);
      },
      error: function(err) {
        if (onError) onError(err);
      },
      complete: function() {
        setTimeout(go, interval);
      }
    });
  }
  go();
}
// usage
jqPoll('/api/status', 4000, data => console.log(data));

// 33. Serialize form and send via AJAX
$('#myform').on('submit', function(e) {
  e.preventDefault();
  const form = $(this);
  $.ajax({
    url: form.attr('action'),
    method: form.attr('method'),
    data: form.serialize(),
    success: function(res) {
      console.log('Submitted:', res);
    }
  });
});

// 34. AJAX file upload using FormData (with jQuery)
$('#uploadForm').on('submit', function(e) {
  e.preventDefault();
  const form = this;
  const fd = new FormData(form);
  $.ajax({
    url: form.action,
    method: form.method,
    data: fd,
    processData: false,
    contentType: false,
    success: function(res) {
      console.log('File uploaded:', res);
    }
  });
});

// 35. Fallback to iframe upload (older browsers) — minimal scaffold
function fallbackIFrameUpload(form, callbackName) {
  const iframe = document.createElement('iframe');
  iframe.name = 'upload_iframe_' + Date.now();
  iframe.style.display = 'none';
  document.body.appendChild(iframe);
  form.target = iframe.name;
  window[callbackName] = function(resp) {
    // cleanup
    delete window[callbackName];
    document.body.removeChild(iframe);
    callback(resp);
  };
  form.submit();
}
// usage: wrap your form and server returns a script calling the callbackName

// 36. Error handler helper
function showAjaxError(xhr, status, err) {
  console.error('AJAX error:', status, err);
  try {
    const resp = JSON.parse(xhr.responseText);
    console.error('Details:', resp);
  } catch (e) {
    console.error('Non-JSON response:', xhr.responseText);
  }
}

// 37. Chain multiple AJAX (nested)
$.ajax({ url: '/api/step1' })
  .done(data1 => {
    return $.ajax({ url: `/api/step2?id=${data1.id}` });
  })
  .done(data2 => {
    console.log('All done:', data2);
  })
  .fail(err => {
    console.error('Chain failed:', err);
  });

// 38. Abort with timeout in jQuery
$.ajax({
  url: '/api/slow',
  timeout: 5000,  // milliseconds
  success: res => console.log(res),
  error: (xhr, status) => {
    if (status === 'timeout') console.warn('Request timeout');
    else console.error('Error:', status);
  }
});

// 39. Prevent double submission (disable button)
$('#submitBtn').on('click', function(e) {
  const btn = $(this);
  btn.prop('disabled', true);
  $.ajax({
    url: '/api/do',
    method: 'POST',
    data: { x: 1 },
    complete: () => btn.prop('disabled', false)
  });
});

// 40. JSON POST with fallback to traditional POST
function postJsonOrForm(url, data) {
  if (window.fetch) {
    return fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
  } else {
    return $.ajax({
      url,
      method: 'POST',
      data: data,
      dataType: 'json'
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Axios / modern libraries

// 41. Basic Axios GET
axios.get('/api/data')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

// 42. Axios POST JSON
axios.post('/api/submit', { name: 'Eve', city: 'LA' })
  .then(resp => console.log(resp.data))
  .catch(err => console.error(err));

// 43. Axios with config (headers, timeout)
axios({
  method: 'post',
  url: '/api/do',
  data: { x: 1 },
  headers: { 'X-Custom': 'MyVal' },
  timeout: 5000
})
.then(r => console.log(r.data))
.catch(err => console.error(err));

// 44. Axios GET with params
axios.get('/api/users', { params: { page: 3 } })
  .then(r => console.log(r.data));

// 45. Axios cancel token (abort)
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/api/long', { cancelToken: source.token })
  .then(r => console.log(r.data))
  .catch(thrown => {
    if (axios.isCancel(thrown)) {
      console.log('Request canceled', thrown.message);
    } else {
      console.error(thrown);
    }
  });
// to cancel:
source.cancel('Operation canceled by user.');

// 46. Axios interceptors (request / response)
axios.interceptors.request.use(config => {
  // e.g. add auth header
  config.headers['Authorization'] = 'Bearer ' + token;
  return config;
}, err => Promise.reject(err));
axios.interceptors.response.use(resp => {
  return resp;
}, err => {
  if (err.response && err.response.status === 401) {
    // handle unauthorized, e.g. redirect to login
  }
  return Promise.reject(err);
});

// 47. Axios concurrent requests (Promise.all)
Promise.all([
  axios.get('/api/a'),
  axios.get('/api/b')
]).then(([a, b]) => {
  console.log(a.data, b.data);
});

// 48. Axios retry with interceptors (simple)
function axiosRetry(instance, retries = 3) {
  instance.interceptors.response.use(null, (error) => {
    const config = error.config;
    if (!config || !config.retryCount) config.retryCount = 0;
    if (config.retryCount >= retries) return Promise.reject(error);
    config.retryCount++;
    return new Promise(resolve =>
      setTimeout(() => resolve(instance(config)), 1000)
    );
  });
}
axiosRetry(axios, 2);

// 49. Axios upload with progress
const form = new FormData();
form.append('file', fileInput.files[0]);
axios.post('/api/upload', form, {
  headers: { 'Content-Type': 'multipart/form-data' },
  onUploadProgress: progressEvent => {
    const pct = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log('Upload:', pct + '%');
  }
}).then(r => console.log(r.data));

// 50. Axios download with blob
axios.get('/api/report', { responseType: 'blob' })
  .then(resp => {
    const url = URL.createObjectURL(resp.data);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'report.pdf';
    a.click();
  });
Enter fullscreen mode Exit fullscreen mode

4. Utilities & Helpers

// 51. Convert object to query string
function toQueryString(obj) {
  return Object.entries(obj)
    .map(([k,v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
    .join('&');
}
// usage: toQueryString({ a:1, b:2 }) → "a=1&b=2"

// 52. Sleep / delay
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 53. Wrap callback-style function into Promise
function promisify(fn) {
  return function(...args) {
    return new Promise((resolve, reject) => {
      fn(...args, (err, res) => {
        if (err) reject(err);
        else resolve(res);
      });
    });
  };
}

// 54. Parse JSON safely
function safeJsonParse(str, fallback = null) {
  try {
    return JSON.parse(str);
  } catch (e) {
    return fallback;
  }
}

// 55. Deep merge of objects (simple)
function deepMerge(...objs) {
  const result = {};
  for (const obj of objs) {
    for (const [k, v] of Object.entries(obj)) {
      if (v && typeof v === 'object' && !Array.isArray(v)) {
        result[k] = deepMerge(result[k] || {}, v);
      } else {
        result[k] = v;
      }
    }
  }
  return result;
}

// 56. Request queue (limit concurrent fetches)
class RequestQueue {
  constructor(maxConcurrency = 3) {
    this.maxConcurrency = maxConcurrency;
    this.running = 0;
    this.queue = [];
  }
  enqueue(fn) {
    return new Promise((res, rej) => {
      this.queue.push({ fn, res, rej });
      this.next();
    });
  }
  next() {
    if (this.running >= this.maxConcurrency) return;
    const job = this.queue.shift();
    if (!job) return;
    this.running++;
    job.fn()
      .then(job.res)
      .catch(job.rej)
      .finally(() => {
        this.running--;
        this.next();
      });
  }
}
// usage
// const rq = new RequestQueue(2);
// rq.enqueue(() => fetch('/api/a')).then(...);

// 57. Once-only wrapper
function once(fn) {
  let called = false;
  let result;
  return function(...args) {
    if (!called) {
      result = fn.apply(this, args);
      called = true;
    }
    return result;
  };
}

// 58. Retry wrapper for fetch-like promises
function withRetry(fn, retries = 2, delayMs = 1000) {
  return async function wrapped(...args) {
    let lastError;
    for (let i = 0; i < retries; i++) {
      try {
        return await fn(...args);
      } catch (err) {
        lastError = err;
        await sleep(delayMs);
      }
    }
    throw lastError;
  };
}

// 59. Cache results of fetch (memoize)
function memoizeFetch(fn) {
  const cache = new Map();
  return async function(url, options) {
    const key = url + JSON.stringify(options || {});
    if (cache.has(key)) {
      return cache.get(key);
    }
    const res = await fn(url, options);
    const data = await res.clone().json();
    cache.set(key, data);
    return new Response(JSON.stringify(data), {
      status: res.status,
      statusText: res.statusText,
      headers: res.headers
    });
  };
}

// 60. Exponential backoff (retry with increasing delay)
async function fetchWithBackoff(url, options = {}, maxRetries = 5, baseDelay = 500) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const res = await fetch(url, options);
      if (!res.ok) throw new Error(`Bad status ${res.status}`);
      return res;
    } catch (err) {
      if (i === maxRetries - 1) throw err;
      const wait = baseDelay * 2 ** i;
      await sleep(wait);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Advanced / composite patterns

// 61. AJAX + UI loading indicator (vanilla)
async function fetchWithLoader(url, container, loaderEl) {
  loaderEl.style.display = 'block';
  try {
    const res = await fetch(url);
    const data = await res.json();
    container.innerText = JSON.stringify(data, null, 2);
  } catch (err) {
    container.innerText = 'Error: ' + err.message;
  } finally {
    loaderEl.style.display = 'none';
  }
}

// 62. Infinite scroll (on scroll bottom, fetch more)
let currentPage = 1;
let loading = false;
window.addEventListener('scroll', async () => {
  if (loading) return;
  if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 200) {
    loading = true;
    try {
      const res = await fetch(`/api/list?page=${++currentPage}`);
      const items = await res.json();
      items.forEach(item => {
        const el = document.createElement('div');
        el.textContent = JSON.stringify(item);
        document.body.appendChild(el);
      });
    } catch (err) {
      console.error('Load more failed:', err);
    }
    loading = false;
  }
});

// 63. Live search / autocomplete
const input = document.querySelector('#searchBox');
input.addEventListener('input', debounce(async (e) => {
  const term = e.target.value;
  if (!term) return;
  const res = await fetch(`/api/search?q=${encodeURIComponent(term)}`);
  const suggestions = await res.json();
  // render suggestions...
  console.log('suggestions:', suggestions);
}, 300));

// 64. Dependent dropdowns (select A => fetch options for B)
$('#selectA').on('change', function() {
  const val = $(this).val();
  $.ajax({
    url: '/api/options',
    data: { parent: val },
    dataType: 'json',
    success: function(arr) {
      const $selB = $('#selectB');
      $selB.empty();
      arr.forEach(o => {
        $selB.append(`<option value="${o.value}">${o.label}</option>`);
      });
    }
  });
});

// 65. AJAX pagination (replace page content)
$('.pagination a').on('click', function(e) {
  e.preventDefault();
  const url = $(this).attr('href');
  $('#content').fadeOut(200, function() {
    $('#content').load(url + ' #content > *', function() {
      $('#content').fadeIn(200);
    });
  });
});

// 66. Submit via AJAX and show inline validation errors
$('#form').on('submit', function(e) {
  e.preventDefault();
  const form = $(this);
  $.ajax({
    url: form.attr('action'),
    method: form.attr('method'),
    data: form.serialize(),
    success: function(res) {
      if (res.errors) {
        for (const [field, msg] of Object.entries(res.errors)) {
          form.find(`[name=${field}]`).after(`<span class="error">${msg}</span>`);
        }
      } else {
        // success path
      }
    }
  });
});

// 67. Optimistic UI update + rollback
async function optimisticUpdate(url, newData, oldData) {
  const backup = { ...oldData };
  // update UI immediately
  Object.assign(oldData, newData);
  try {
    const res = await fetch(url, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newData)
    });
    if (!res.ok) throw new Error('Save failed');
  } catch (err) {
    // rollback
    Object.assign(oldData, backup);
    console.error('Optimistic update failed:', err);
  }
}

// 68. Partial HTML update (patch)
async function patchHtml(selector, url) {
  const res = await fetch(url);
  const html = await res.text();
  document.querySelector(selector).innerHTML = html;
}

// 69. JSON PATCH with REST
fetch('/api/item/123', {
  method: 'PATCH',
  headers: { 'Content-Type': 'application/json-patch+json' },
  body: JSON.stringify([
    { op: 'replace', path: '/name', value: 'New name' }
  ])
}).then(r => r.json()).then(data => console.log(data));

// 70. Batching multiple API calls to single endpoint
async function batchFetch(calls) {
  // calls = [{ url, method, body }, ...]
  const res = await fetch('/api/batch', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ calls })
  });
  return res.json();  // expect array of responses
}
// usage
// batchFetch([{ url: '/u/1', method: 'GET' }, { url: '/u/2', method: 'GET' }])
//   .then(results => { ... });

// 71. Progressive enhancement: fallback to normal navigation when JS disabled
document.querySelectorAll('a.ajax').forEach(a => {
  a.addEventListener('click', e => {
    e.preventDefault();
    fetch(a.href)
      .then(r => r.text())
      .then(html => {
        document.getElementById('main').innerHTML = html;
        // update URL
        window.history.pushState({}, '', a.href);
      });
  });
});
window.addEventListener('popstate', () => {
  fetch(location.href)
    .then(r => r.text())
    .then(html => {
      document.getElementById('main').innerHTML = html;
    });
});

// 72. Using POST but simulate redirect (for APIs that respond with redirect info)
$.ajax({
  url: '/api/login',
  method: 'POST',
  data: { user: 'u', pass: 'p' },
  success: function(res) {
    if (res.redirectTo) window.location = res.redirectTo;
    else console.log('Login result', res);
  }
});

// 73. Cancel an in-flight request when new one is made (debounce + cancel)
let lastRequest = null;
function doSearch(term) {
  if (lastRequest) lastRequest.abort();
  lastRequest = new AbortController();
  fetch(`/api/search?q=${encodeURIComponent(term)}`, {
    signal: lastRequest.signal
  })
    .then(r => r.json())
    .then(data => console.log('search:', data))
    .catch(err => {
      if (err.name === 'AbortError') console.log('Search aborted');
      else console.error(err);
    });
}

// 74. WebSocket fallback: if server supports real-time updates
const ws = new WebSocket('wss://example.com/ws');
ws.onopen = () => console.log('Socket open');
ws.onmessage = msg => {
  const data = JSON.parse(msg.data);
  console.log('WS message:', data);
};
ws.onerror = err => console.error('WS error:', err);

// 75. Detect AJAX complete on document (jQuery)
$(document).ajaxComplete((event, xhr, settings) => {
  console.log('AJAX complete:', settings.url, xhr.status);
});
Enter fullscreen mode Exit fullscreen mode

6. Error handling, retries, timeouts, fallback

// 76. Centralized fetch wrapper with error translation
async function safeFetch(url, options) {
  try {
    const res = await fetch(url, options);
    if (!res.ok) {
      const text = await res.text();
      throw new Error(`HTTP ${res.status}: ${text}`);
    }
    return res;
  } catch (err) {
    console.error('safeFetch error:', err);
    throw err;
  }
}

// 77. Retry with jitter
async function fetchWithJitter(url, options = {}, retries = 4, baseDelay = 300) {
  for (let i = 0; i < retries; i++) {
    try {
      const res = await fetch(url, options);
      if (!res.ok) throw new Error(`Bad status ${res.status}`);
      return res;
    } catch (err) {
      if (i === retries - 1) throw err;
      const jitter = Math.random() * baseDelay;
      await sleep(baseDelay * i + jitter);
    }
  }
}

// 78. Fallback to JSONP if CORS fails (vanilla)
function jsonp(url, callbackName) {
  return new Promise((resolve, reject) => {
    const cb = `jsonp_cb_${Date.now()}`;
    window[cb] = data => {
      resolve(data);
      delete window[cb];
    };
    const s = document.createElement('script');
    s.src = `${url}${url.includes('?') ? '&' : '?'}${callbackName}=${cb}`;
    s.onerror = reject;
    document.body.appendChild(s);
  });
}
// usage: jsonp('https://api.example.com/data', 'callback').then(data => ...)

// 79. Fallback content if AJAX fails
fetch('/api/part')
  .then(r => {
    if (!r.ok) throw new Error('Bad fetch');
    return r.text();
  })
  .then(html => {
    document.getElementById('sidebar').innerHTML = html;
  })
  .catch(err => {
    console.warn('Fallback: show default');
    document.getElementById('sidebar').innerHTML = '<p>Offline content here</p>';
  });

// 80. Catch-all global fetch error (monkey patch)
const _oldFetch = window.fetch;
window.fetch = function(...args) {
  return _oldFetch(...args).catch(err => {
    console.error('Global fetch error:', err, 'for args:', args);
    throw err;
  });
};
Enter fullscreen mode Exit fullscreen mode

7. Miscellaneous tricks & small helpers

// 81. Listen to any AJAX success (jQuery)
$(document).ajaxSuccess((e, xhr, settings) => {
  console.log('Successful AJAX:', settings.url);
});

// 82. Serialize form as JSON
function serializeFormToJSON(formElement) {
  const form = new FormData(formElement);
  const obj = {};
  for (const [k, v] of form.entries()) {
    obj[k] = v;
  }
  return obj;
}

// 83. Cancelable promise wrapper (for fetch or AJAX)
function cancelablePromise(promise) {
  let canceled = false;
  const wrapped = new Promise((res, rej) => {
    promise.then(
      val => canceled ? rej({ canceled: true }) : res(val),
      err => canceled ? rej({ canceled: true }) : rej(err)
    );
  });
  return {
    promise: wrapped,
    cancel() { canceled = true; }
  };
}
// usage
// const c = cancelablePromise(fetch('/api'));
// c.promise.then(...).catch(e => { if (e.canceled) … });
// c.cancel();

// 84. Add timestamp to URL to prevent caching
function cacheBustingUrl(url) {
  const sep = url.includes('?') ? '&' : '?';
  return `${url}${sep}_=${Date.now()}`;
}
// usage: fetch(cacheBustingUrl('/api/data'));

// 85. Proxy to wrap fetch to always send credentials
const fetchWithCreds = (url, options = {}) => {
  return fetch(url, { ...options, credentials: 'include' });
};

// 86. Monitoring long requests (log if > threshold)
async function monitoredFetch(url, options = {}, threshold = 2000) {
  const start = Date.now();
  const res = await fetch(url, options);
  const elapsed = Date.now() - start;
  if (elapsed > threshold) {
    console.warn(`Request to ${url} took ${elapsed}ms`);
  }
  return res;
}

// 87. Refresh token on 401 and retry
async function fetchWithAuth(url, options = {}) {
  let res = await fetch(url, options);
  if (res.status === 401) {
    // assume refresh token logic
    await fetch('/api/refresh', { method: 'POST' });
    res = await fetch(url, options);
  }
  return res;
}

// 88. Lazy load a script only when needed (inject and await)
function loadScript(src) {
  return new Promise((res, rej) => {
    const s = document.createElement('script');
    s.src = src;
    s.onload = res;
    s.onerror = rej;
    document.head.appendChild(s);
  });
}

// 89. Progressive fetch (fetch first few, then rest)
async function progressiveLoad(urls) {
  const first = await fetch(urls[0]).then(r => r.json());
  console.log('First loaded:', first);
  for (let i = 1; i < urls.length; i++) {
    fetch(urls[i])
      .then(r => r.json())
      .then(data => console.log('Loaded:', data))
      .catch(err => console.error(err));
  }
}

// 90. Fire-and-forget (ignore result, but catch errors)
async function fireAndForget(url, options = {}) {
  fetch(url, options).catch(err => console.error('FF error:', err));
}
// usage: fireAndForget('/api/log', { method: 'POST', body: ... });

// 91. CORS preflight check (OPTIONS)
fetch('/api/data', {
  method: 'OPTIONS'
}).then(r => {
  console.log('Allowed methods:', r.headers.get('Allow'));
});

// 92. Fetch local JSON file
fetch('/assets/data.json')
  .then(r => r.json())
  .then(data => console.log(data));

// 93. Load CSS file dynamically after fetch
async function loadCssAfterFetch(cssUrl, gateUrl) {
  await fetch(gateUrl);
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = cssUrl;
  document.head.appendChild(link);
}

// 94. Chunked download (range requests)
async function fetchRange(url, start, end) {
  const res = await fetch(url, {
    headers: { Range: `bytes=${start}-${end}` }
  });
  return res.arrayBuffer();
}

// 95. JSON to FormData conversion
function jsonToFormData(obj, fd = new FormData(), prefix = '') {
  for (const [k, v] of Object.entries(obj)) {
    const key = prefix ? `${prefix}[${k}]` : k;
    if (v && typeof v === 'object' && !(v instanceof File)) {
      jsonToFormData(v, fd, key);
    } else {
      fd.append(key, v);
    }
  }
  return fd;
}

// 96. Check if browser supports fetch, fallback to XHR
function myFetch(url, options) {
  if (window.fetch) {
    return fetch(url, options);
  } else {
    return new Promise((resolve, reject) => {
      xhrGet(url, (err, text) => {
        if (err) reject(err);
        else resolve({ text: () => text });
      });
    });
  }
}

// 97. Rate limiting requests
function rateLimit(fn, limit = 1000) {
  let last = 0;
  return function(...args) {
    const now = Date.now();
    if (now - last < limit) {
      return Promise.reject(new Error('Rate limit exceeded'));
    }
    last = now;
    return fn(...args);
  };
}

// 98. Wrap fetch to always return JSON or throw
async function fetchJsonOrThrow(url, options) {
  const res = await fetch(url, options);
  if (!res.ok) {
    const msg = await res.text();
    throw new Error(`HTTP ${res.status}: ${msg}`);
  }
  return res.json();
}

// 99. Lazy-loaded infinite scroll (trigger only when scroll region is visible)
function initInfiniteScroll(container, fetchFn) {
  const observer = new IntersectionObserver(entries => {
    if (entries[0].isIntersecting) {
      fetchFn();
    }
  });
  observer.observe(container);
}

// 100. AJAX + form reset + notification
$('#myForm').on('submit', function(e) {
  e.preventDefault();
  const frm = $(this);
  $.ajax({
    url: frm.attr('action'),
    method: frm.attr('method'),
    data: frm.serialize()
  }).done(res => {
    alert('Saved!');
    frm[0].reset();
  }).fail(err => {
    alert('Error: ' + err.statusText);
  });
}
Enter fullscreen mode Exit fullscreen mode

8. Minimal full examples ready to drop in

<!-- 101. Minimal fetch demo -->
<button id="loadBtn">Load Data</button>
<div id="output"></div>

<script>
document.getElementById('loadBtn').addEventListener('click', () => {
  fetch('/api/hello')
    .then(r => r.json())
    .then(data => {
      document.getElementById('output').textContent = JSON.stringify(data);
    })
    .catch(e => {
      document.getElementById('output').textContent = 'Error: ' + e;
    });
});
</script>
Enter fullscreen mode Exit fullscreen mode
<!-- 102. jQuery AJAX demo -->
<form id="demoForm" action="/api/echo" method="post">
  <input name="msg" placeholder="Message" />
  <button type="submit">Send</button>
</form>
<div id="resp"></div>

<script>
$('#demoForm').on('submit', function(e) {
  e.preventDefault();
  const f = $(this);
  $.ajax({
    url: f.attr('action'),
    method: f.attr('method'),
    data: f.serialize(),
    success: function(res) {
      $('#resp').text('Server says: ' + JSON.stringify(res));
    },
    error: function(xhr) {
      $('#resp').text('Error: ' + xhr.statusText);
    }
  });
});
</script>
Enter fullscreen mode Exit fullscreen mode
// 103. Debounced search + fetch demo (vanilla)
const input = document.getElementById('search');
input.addEventListener('input', debounce(async (e) => {
  const q = e.target.value;
  const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
  const arr = await res.json();
  console.log('Results:', arr);
}, 300));
Enter fullscreen mode Exit fullscreen mode

Top comments (0)