Last active
February 27, 2022 01:35
-
-
Save DavidWells/ba3cbeb5ca8e486eff839fa9dcc0f549 to your computer and use it in GitHub Desktop.
REST JS proxy implementation based on https://gist.github.com/v1vendi/75d5e5dad7a2d1ef3fcb48234e4528cb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fetch = async (...args) => { | |
console.log(...args) | |
return { | |
statusCode: 200, | |
data: {}, | |
} | |
} | |
function httpRequest(url, method, data, opts, cb) { | |
const init = { method } | |
switch (method) { | |
case 'GET': | |
if (data) { | |
url = `${url}?${new URLSearchParams(data)}` | |
} | |
break | |
case 'POST': | |
case 'PUT': | |
case 'PATCH': | |
init.body = JSON.stringify(data) | |
} | |
// Run promise or callback | |
return fetch(url, init).then((d) => { | |
if (cb) return cb(undefined, d) | |
return d | |
}).catch((err) => { | |
if (cb) return cb(err) | |
throw err | |
}) | |
} | |
function generateAPI(url, scope = []) { | |
// a hack, so we can use field either as property or a method | |
const callable = () => {} | |
callable.url = url | |
return new Proxy(callable, { | |
get({ url }, propKey) { | |
const upperKey = propKey.toUpperCase() | |
const prevScope = scope[scope.length - 1] | |
const path = scope.concat(propKey) | |
console.log('prevScope', prevScope) | |
if (['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(upperKey)) { | |
const resolvedPath = path.slice(0, -1) | |
console.log('resolved SDK path', resolvedPath) | |
const func = (data, options = {}, callback) => { | |
console.log('data', data) | |
console.log('options', options) | |
const config = typeof options === 'function' ? {} : options | |
const cb = typeof options === 'function' ? options : callback | |
return httpRequest(url, upperKey, data, config, cb) | |
} | |
/* Add callable sub keys after HTTP method */ | |
func.sync = (data, options = {}) => { | |
console.log('run sync thing', data) | |
console.log('run sync opts', options) | |
} | |
/* Client signature */ | |
return func | |
} | |
return generateAPI(`${url}/${propKey}`, path) | |
}, | |
apply({ url }, thisArg, [arg] = []) { | |
// Mid chain function call () | |
// e.g. client.product(7).get() | |
console.log('apply called', url) | |
console.log('arg used', arg) | |
const path = url.split('/') | |
return generateAPI(arg ? `${url}/${arg}` : url, path) | |
} | |
}) | |
} | |
/* Init client */ | |
const client = generateAPI('https://base.com') | |
/* Usage example client.thing.xyz.operation() */ | |
client.get() // GET https://base.com | |
client.post() // PUT https://base.com | |
client.put() // POST https://base.com | |
client.delete() // DELETE https://base.com | |
const opts = { foo: 'bar' } | |
/* Mid chain calls with () work too but look weird */ | |
// GET https://base.com/product/7 | |
client.product(7).get() | |
// GET with query strings https://base.com/base/tiles/public/static/2.json?turn=37038&games=wot | |
client.product(7).get({ | |
turn: 37, | |
games: 'wot' | |
}) | |
// DELETE https://base.com/product/7/whatever | |
client.product(7).whatever.delete(1, opts) | |
// PUT with body https://base.com/product/7 | |
client.product(7).put({ | |
whatever: 1 | |
}) | |
// POST with body https://base.com/product/7 w/ body | |
client.product(7).post({ | |
whatever: 1 | |
}) | |
// GET https://base.com/base/tiles/public/static/3/4/2.json | |
client.tiles.public.static(3)(4)(`${2}.json`).get() | |
/* Bracket syntax for numbers */ | |
// GET https://base.com/product/7 | |
client.product[7].get() | |
// GET with query strings https://base.com/base/tiles/public/static/2.json?turn=37038&games=wot | |
client.product[7].get({ | |
turn: 37, | |
games: 'wot' | |
}) | |
// DELETE https://base.com/product/7/whatever | |
client.product[7].whatever.delete(1, opts) | |
// PUT with body https://base.com/product/7 | |
client.product[7].put({ | |
whatever: 1 | |
}) | |
// POST with body https://base.com/product/7 | |
client.product[7].post({ | |
whatever: 1 | |
}) | |
/* Clean API */ | |
// GET https://base.com/entities/product | |
client.entities.product.get() | |
// GET with query strings https://base.com/entities/product?turn=37038&games=wot | |
client.entities.product.get({ | |
turn: 37, | |
games: 'wot' | |
}) | |
// DELETE https://base.com/entities/product | |
client.entities.product.delete(1, opts) | |
// PUT with body https://base.com/entities/product | |
client.entities.product.put({ | |
whatever: 1 | |
}) | |
// POST with body https://base.com/entities/product | |
client.entities.product.post({ | |
whatever: 1 | |
}) | |
const payload = { | |
foo: 'bar' | |
} | |
/* Callback Usage */ | |
client.entity.shape[1].post(payload, (err, resp) => { | |
console.log('callback', resp) | |
}) | |
/* Promise Usage */ | |
client.entity.shape.post(payload).then((resp) => { | |
console.log('promise', resp) | |
}) | |
/* Sync Usage */ | |
client.entity.shape.post.sync(payload) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment