-
-
Save DavidWells/53518b3c12344952641dc81cc7599939 to your computer and use it in GitHub Desktop.
/* Using a JavaScript proxy for a super low code REST client */ | |
// via https://dev.to/dipsaus9/javascript-lets-create-aproxy-19hg | |
// also see https://towardsdatascience.com/why-to-use-javascript-proxy-5cdc69d943e3 | |
// also see https://github.com/fastify/manifetch | |
// also see https://github.com/flash-oss/allserver | |
// and https://gist.github.com/v1vendi/75d5e5dad7a2d1ef3fcb48234e4528cb | |
const createApi = (url) => { | |
return new Proxy({}, { | |
get(target, key) { | |
return async function(id = "") { | |
const response = await fetch(`${url}/${key}/${id}`) | |
if (response.ok) { | |
return response.json(); | |
} | |
return Promise.resolve({ error: "Malformed Request" }) | |
} | |
} | |
}) | |
} | |
let api = createApi("https://swapi.co/api") | |
// 'get' request to https://swapi.co/api/people | |
let people = await api.people() | |
// 'get' request to https://swapi.co/api/people/1 | |
let person = await api.people(1) | |
// ... any https://swapi.dev/ endpoint |
Oops @md2perpe good catch fixed the syntax error
Really nice but what about TS & auto-complete...
@eladcandroid Couldn't you just cast it to a defined interface?
let api:PeopleApi = createApi("https://blah.de.blah")
const createApi = (url: string): {
[key: string]: (id?: string) => Promise<any>;
} => {
return new Proxy({}, {
get(target, key: string) {
return async function(id = "") {
const response = await fetch(`${url}/${key}/${id}`)
if (response.ok) {
return response.json();
}
return Promise.resolve({ error: "Malformed Request" })
}
}
})
}
createApi('http://myapi.com').somethingOtherThanPeople('moose')
I've created a fully typed version of this approac: https://github.com/johannschopplich/uncreate
I've created a fully typed version of this approac: https://github.com/johannschopplich/uncreate
Oh, nice.. I did the same as a little exercise to myself.. but, I taken it a little bit further..
made it so you can do
const api1 = createApi('https://example.com);
const response = await api1.call.some.deep.path.with[123].numbers.in.the.middle(); // GET https://example.com/call/some/deep/path/with/123/numbers/in/the/middle
// returns a response object.
const defaults = {
method: 'GET',
headers: {
'token': 'my-api-token-string'
},
query: {
'myself': 'i love it'
},
responseType: 'json' // blob/form-data/text/response/json
};
const api2 = createApi('https://example.com', defaults);
const json = api2.first('part')['of']('the/path').goes.here(); // GET https://example.com/first/part/of/the/path/goes/here?myself=i%20love%20it // with token header my-api-token-string
// returns a json object
const text = api2.send('some data', 'and more data')({ responseType: 'text', method: 'POST', body: { 'hay' }, header: { token: 'yay' } }); // POST https://example.com/send/some%20data/and%20more%20data?myself=i%20love%20it // with token header 'yay' // body data
// returns text string.
All typed and knows the return types.
@WORMSS Wow, that's pretty neat. A lot of inspiration. I took some ideas and implemented them into
uncreate
. 🙂
Thank you very much.. Updated Typescript Playground
I actually modified it a little after re-looking at the code yesterday with fresh eyes..
I made it so the headers are merged.. so rather than one or the other.
I also removed the possibility of responseType cobbling, since it was originally calculated after the fetch was returned.
Just small cleanups, nothing major..
I just noticed in your code, that you had arrayBuffer
which was a responseType I missed.. so thank you for that.
Does this line really work? Or is there a comment token
//
missing?