Skip to content

Instantly share code, notes, and snippets.

@jjhiggz
Last active July 12, 2025 11:57
Show Gist options
  • Save jjhiggz/20208f107be7bc1a5f2324c24d170461 to your computer and use it in GitHub Desktop.
Save jjhiggz/20208f107be7bc1a5f2324c24d170461 to your computer and use it in GitHub Desktop.
A simple function fetching pattern
export const getAuthHeaders = (
// Whatever inputs you need
) => {
return {
// whatever object you need
["ContentType"]: "application/json"
}
}
import { logTime, parse, throwIfNotOk, tap} from "./thennables"
import { getAuthHeaders } from "./fetch-options
type Character = {
id: number;
name: string;
url: string;
}
type CharacterResult = {
results: Character[]
}
const getCharacters = () => {
return fetch("https://rickandmortyapi.com/api/character", {
headers: getAuthHeaders()
})
// I personally wouldn't bother testing these because there's not really business logic it's
// all declaritive and extremely easy to read assuming that each function does what it's supposed to
.then(throwIfNotOk("Could not fetch characters"))
.then(logTime("Characters"))
.then(t => parse<CharacterResult>(t))
.then(data => data.results)
.then(tap("Final Value of characters"))
}
getCharacters()
import { describe, it, expect, vi } from 'vitest'
import { throwIfNotOk, logTime, tap } from './thennables'
describe('throwIfNotOk', () => {
it('returns response if ok is true', () => {
const response = { ok: true } as Response
const result = throwIfNotOk('failed')(response)
expect(result).toBe(response)
})
it('throws an error if ok is false', () => {
const response = { ok: false } as Response
expect(() => throwIfNotOk('fail message')(response)).toThrowError('fail message')
})
})
describe('logTime', () => {
it('logs Date.now and tag and returns the response', () => {
const nowSpy = vi.spyOn(Date, 'now').mockReturnValue(123456789)
const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {})
const response = {} as Response
const tag = 'myTag'
const result = logTime(tag)(response)
expect(consoleSpy).toHaveBeenCalledWith(123456789, tag)
expect(result).toBe(response)
nowSpy.mockRestore()
consoleSpy.mockRestore()
})
})
describe('tap', () => {
it('logs the tag and value and returns the value', () => {
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
const tag = 'debug'
const value = { foo: 'bar' }
const result = tap(tag)(value)
expect(consoleSpy).toHaveBeenCalledWith(`${tag}: `, value)
expect(result).toBe(value)
consoleSpy.mockRestore()
})
})
// thennables.ts
// you can write tests for these because there's business logic (not that I would because they are dead simple)
export const throwIfNotOk = (message: string) => (response: Response) => {
if(!response.ok){
throw new Error(message)
}
return response
}
export const logTime = (tag: string) => (response: Response) => {
console.info(Date.now(), tag)
return response
}
export const tap = (tag: string) => <T,>(input: T) => {
console.log(`${tag}: `, input)
return input
}
export const parse = <T,>(response: Response) => response.json() as Promise<T>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment