Skip to content

Instantly share code, notes, and snippets.

@DavidWells
Last active October 6, 2023 18:39
Show Gist options
  • Save DavidWells/53518b3c12344952641dc81cc7599939 to your computer and use it in GitHub Desktop.
Save DavidWells/53518b3c12344952641dc81cc7599939 to your computer and use it in GitHub Desktop.
Using a javascript proxy as low code REST client
/* 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
@DavidWells
Copy link
Author

Oops @md2perpe good catch fixed the syntax error

@eladcandroid
Copy link

Really nice but what about TS & auto-complete...

@hyrumwhite
Copy link

@eladcandroid Couldn't you just cast it to a defined interface?

let api:PeopleApi = createApi("https://blah.de.blah")

@WORMSS
Copy link

WORMSS commented Feb 20, 2022

@eladcandroid

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')

🔗 TypeScript Playground

image

@johannschopplich
Copy link

I've created a fully typed version of this approac: https://github.com/johannschopplich/uncreate

@WORMSS
Copy link

WORMSS commented Mar 14, 2022

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..

Typescript Playground

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.

@johannschopplich
Copy link

@WORMSS Wow, that's pretty neat. A lot of inspiration. I took some ideas and implemented them into uncreate. 🙂

@WORMSS
Copy link

WORMSS commented Mar 15, 2022

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment