osuAkatsuki / bancho.py

An osu! server for the generic public, optimized for maintainability in modern python
https://akatsuki.gg
MIT License
214 stars 131 forks source link

API v2 Proposal #147

Open memsdm05 opened 2 years ago

memsdm05 commented 2 years ago

sup gamers,

I come from my inactivity to outline some improvements and refactoring for Gulag's API. Thanks to FastAPI, it is possible to implement some improvements that were much harder to do with cmyui_pkg. In my opinion, I think the current API (hereby referred to as v1) is should be deprecated and replaced with a v2. That being said, here are my takes for a future API.

In General

REST

Here are all v1 endpoints at the time of writing: https://github.com/cmyui/gulag/blob/e40ed8921eb62a7561ecb6d5042e7062bb6b9a61/mount/app/api/domains/api.py#L45-L63

Proposed Changes

:mode = <osu | mania | catch | taiko>
GET /api/v2/online : return total registered & online player counts. (? I have to think about this one)
GET /api/v2/player/:id : return info or stats for a given player.
GET /api/v2/player/:id/status : return a player's current status, if online. (should be a WebSocket)
GET /api/v2/player/:id/scores/<best | recent | most_played> :  return a list of best, recent, or most played scores for a given player.

GET /api/v2/map/:id : return information about a given beatmap
GET /api/v2/map/:id/:mode : return information about a given beatmap for a mode.
GET /api/v2/map/:id/:mode/scores : return the best scores for a given beatmap & mode.

GET /api/v2/mapset/:id : return information about a given beatmapset
GET /api/v2/mapset/:id/:mode : return information about a given beatmapset for a mode.
GET /api/v2/mapset/:id/:mode/scores : return the best scores for a given beatmap & mode.

GET /api/v2/score/:id : return information about a given score.
GET /api/v2/score/:id/dl: return the file for a given replay (with or without headers).
GET /api/v2/match/:id : return information for a given multiplayer match.

GET /api/v2/leaderboard/<score | pp>/:mode : return the top players for a given mode & sort condition
GET /api/v2/leaderboard/country/:country_code/<score | pp>/:mode

mode might be optional, just defaults to osu!std

GET /api/v2/map/:id/calc/pp : calculates pp
GET /api/v2/map/:id/calc/score : calculates largest possible score

GET /api/v2/me : user information about oneself
PUT /api/v2/me/avatar : upload an avatar

GET /api/v2/search/players : searches players
GET /api/v2/search/maps : searches maps
GET /api/v2/search/mapsets : searches mapsets
GET /api/v2/search/scores : searches scores scores

GET /api/v2/player/from/:name : returns a player response / id from a name

Reasoning

see osu!api v2

Get rid of "monopaths" and use a RESTful path system. Instead of a single endpoint returning a certain JSON response, every model should have child paths that return related responses. /get_user_info would become /user, /get_player_scores would become /player/scores, etc. The get_ prefix is redundant, as any request other than an HTTP GET should be a 405 error. _info endpoints can be moved to the root model path (i.e. /user, /beatmap, /score). When requesting models that have an Id (aka all of them), it would be better to pass their Id in the path (i.e /user/124493). In my mind path variables are required whereas query parameters are optional. Models should have two types of response structures: Compact and Full. The full structure is returned whenever the model is by itself, while compact is returned when it is in a list. Search endpoints should return a list of compact responses. If one needs more information one must request the full response.

Pros

Cons

GraphQL

If you're unfamiliar, please take a look at the offical guide. It's a good read.

example query

query {
     user(id: 124493) {
          id
          name
          created_at
          scores(type: RECENT) {
               map {
                    title
               }
               pp
          }
     }
}

example response

{
     "data": {
          "user": {
               "id": 124492,
               "name": "chocomint",
               "created_at": "2021-12-06T04:49:25+00:00",
               "scores": [    // blue zenith joke haha so funny
                    {
                         "map": {
                              "title": "Blue Zenith"
                         }
                         "pp": 727.21
                    }
               ]
           },
      },
}

Reasoning

Imagine taking all the previous endpoints and condensing them into one: /graphql. Yep. Instead of having a path represent an entity, root queries that represent certain types of REST endpoints (in a way). For example, say we want to query a single user. We could have a query along the lines of user(id: 124493). Say we wanted multiple users. We can just do something like users(ids: [124493, 3]). This allows for much more flexibility. Also, no versioning!.

@cmyui I'd be happy to code up GraphQL. We can talk semantics on Discord.

Pros

Cons

Final Notes

It's late, I'm tired, I'll keep this short. We should pick either REST or GraphQL. I think having both would be complicated: stick to one and do it well. I'm on side GraphQL. Ok bye.

ghost commented 2 years ago