Skip to content

Search-as-you-type

Fire a request per keystroke and responses come back out of order, so a stale result can overwrite a newer one. abortPrevious cancels the previous in-flight request whenever a new one starts.

The client

ts
import { 
corgi
,
isAbortError
} from '@itsy/corgi';
import {
abortPrevious
} from '@itsy/corgi/abort-previous';
import {
withTimeout
} from '@itsy/corgi/timeout';
interface Result {
id
: string;
title
: string;
} const
search
=
corgi
.
create
({
baseURL
: 'https://api.example.com',
plugins
: [
abortPrevious
(),
withTimeout
(5000)],
}); async function
runSearch
(
q
: string):
Promise
<Result[] | undefined> {
try { return await
search
.
get
<Result[]>('/search', {
query
: {
q
} });
} catch (
err
) {
// The superseded request rejects with an AbortError — that's expected, ignore it. if (
isAbortError
(
err
)) return
undefined
;
throw
err
; // a real failure
} }

Every call to runSearch supersedes the last: the older request is aborted (its promise rejects with AbortError), and only the most recent one can resolve. No manual AbortController bookkeeping.

Keep the client stable

abortPrevious is stateful, remembering the current request in the pipeline built by corgi. Create the client once (module scope in the browser, or per user session), not per keystroke, or there's no shared state to cancel against.

On a server, build it per request

A shared module-scope client would let unrelated users cancel each other. In a request handler:

ts
function 
handler
() {
const
search
=
corgi
.
create
({
plugins
: [
abortPrevious
()] }); // fresh per request
// ...use search for this request only... }

Released under the MIT License.