#api architecture help

11 messages · Page 1 of 1 (latest)

gusty fulcrum
#

I am building an api and i want to be able to call use it like shown below. I want a top level API class, and then each section will be its own submodule, like api.auth will be another imported module, etc. How can i best construct this so that all of the children modules have data from the API class necessary to make calls, such as a baseUrl, and access_token data, etc? The constructor of the API class will take the config and populate a baseUrl field on the api instance object, and I need a way for all child modules to have access to it. I thought about passing it in as a prop and keep following it all the way down, but that just seems clunky

// how do we get the api?
// const api = new API(config);

// how do we use the api?
// api.auth.authorize()
// api.auth.token()
// api.auth.refresh()
// api.shopper.baskets.create()
// api.shopper.customers.create()
// api.shopper.customers.get()
#

I dont want to have everything in a single module, but splitting things up does make it more convoluted to get the necessary config data to everything

delicate bolt
#

are the other sub-modules (like api.auth) classes? or do you not care either way?

#

anyway, i think you will have to pass the API instance (or whatever parts of it you need) down into them. here's an example that demonstrates both class-based and non-class-based modules for them:

arctic patrolBOT
#
mkantor#0

Preview:```ts
type Config = {
hostname: string
}

class API {
baseUrl
auth
shopper
constructor(config: Config) {
this.baseUrl = new URL(
https://${config.hostname}/api
)
this.auth = new auth.Auth(this)
this.shopper = shopper.makeShopper(this)
}
}
...```

gusty fulcrum
#

I ended up making them classes just to handle the dependency injection, but I didn't care either way

#

thank you for this

#

in additiona to this, I've created a method on the top level api class to make requests. eg all of the child api's will call this request method, and pass in their relevant information for the request so that I don't have to duplicate a lot of boiler plate, such as including headers and things. I'm having an issue with the result type from my generic request method, as it is Promise<any> because its literally a request executor that could send any request to anywhere

#

in my individual callee methods I want to transform the response to the appropriate types

delicate bolt
#

the "correct" method is to return Promise<unknown> from the function that actually makes the requests and then in your specific callee methods do runtime checks to decode the response into what you expect (many people use libraries like https://zod.dev/ or https://valibot.dev/ for this)

#

but it depends how much you trust the server. you could also just assert the type and hope for the best if you don't want to do that