Why does my debounced input handler sometimes use the wrong value?

Yo folks, I’m wiring up a search box in a vanilla JS page and trying to debounce the input event so I don’t spam the API, but sometimes it fetches using an older query even though the UI shows the latest text.

const input = document.querySelector('#q');
let timer;

input.addEventListener('input', (e) => {
  const q = e.target.value;
  clearTimeout(timer);
  timer = setTimeout(async () => {
    const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
    render(await res.json());
  }, 250);
});

What’s the cleanest way to guarantee only the newest keystroke wins (including slow network responses) without making the code super tangled?