SinisterRectus / Discordia

Discord API library written in Lua for the Luvit runtime environment
MIT License
700 stars 144 forks source link

More granular indirection needed in Discordia's REST/HTTP API #252

Open SinisterRectus opened 4 years ago

SinisterRectus commented 4 years ago

The Discord API is split into two major protocols: a REST API over HTTPS and a real-time gateway API over WSS.

Discordia has several layers of abstraction between the user and Discord's REST API.

At the lowest layer, Discordia depends on the coro-http library to make HTTPS requests, where raw strings and tables are submitted and returned by a single awaitable function call. This is simple and easy to use, but for a Discord API user, extra work must be done to prepare the requests. This work is handled by Discordia's API class.

In the API class, there is a unique method for each documented Discord API endpoint. These methods accept and normalize variable user input for endpoint parameters plus relevant payload and query information. The normalized data is then passed to a central request method that does the URL construction, JSON and multipart encoding, request header population, and pre-request ratelimiting. The data is then passed to a commit method that makes the request and does the JSON decoding, response header consumption, error handling, and post-request ratelimiting. The response is returned back the individual endpoint method as a JSON data table or an error string in case of failure.

These API methods are not exposed to the user. Instead, they are used throughout the various public classes. For example, users may call Guild:createRole, which calls API:createGuildRole internally and returns a Role object on success. In this case, there is no way to access the raw JSON input or output and there is no way to create a new role without first having access to the full guild object (the Discord API only requires the guild ID). Additionally, Discordia generally only supports editing one object property at a time while internal methods edit many at once. For example, Role:setName and Role:setColor must be called individually, yet both corresponding properties are edited by the same internal Role:_modify and API:modifyGuildRole methods. For some users, it may be beneficial to have access to raw JSON input/output, it may be beneficial to "hierarchy jump" (for example, to create or edit a role without having a guild object), and it may be beneficial to modify properties in bulk instead of with individually abstracted methods. For these reasons, it makes sense to consider more granular indirection in designing the REST part of Discordia 3.0.

SinisterRectus commented 4 years ago

I'm thinking about this more and I'm realizing that it adds a lot of complexity that I don't want to add at the moment. I'll see if I can find some compromise. That may include just allowing access to the raw API class or at least exposing some bulk modify methods.

SinisterRectus commented 4 years ago

A draft of this has actually been implemented in 3.0. See commit 1ccbd03b85601ed6717bfcaf1b0e8da2267a4325. I will leave this issue open for now.

SinisterRectus commented 3 years ago

Almost a year after I dismissed this idea, I am finding that it now makes the library development a bit easier. It's extra work to maintain client methods plus individual container methods, but it is easier to keep track of everything that the library can do when the core functionality is located in one place.