-
Notifications
You must be signed in to change notification settings - Fork 900
chore: idea: unify http responses further #941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## main #941 +/- ##
==========================================
- Coverage 66.22% 61.56% -4.67%
==========================================
Files 240 107 -133
Lines 14408 1137 -13271
Branches 115 115
==========================================
- Hits 9542 700 -8842
+ Misses 3904 412 -3492
+ Partials 962 25 -937
Continue to review full report at Codecov.
|
codersdk/gitsshkey.go
Outdated
response := httpapi.Response{ | ||
Data: &data, | ||
} | ||
err = json.NewDecoder(res.Body).Decode(&response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works? TIL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
json pkg be cray
Agree on a standard |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think httpapi.Response
is our unified response pattern. Putting all message data inside a data
field may promote sending a message
or error
along with a payload, which I'm not sure we have a use case for right now.
I'm not opposed to standardizing render
, and I'd be fine with adding httpapi.Write
with the third argument being an interface{}
that we marshal for the caller.
By moving all data inside a data
property, we wouldn't standardize the payload that our API consumers expect - we move the problem to the data
field. API consumers can use the status code to check if the data is truly what we say it is.
coderd/httpapi/httpapi.go
Outdated
Errors []Error `json:"errors,omitempty" validate:"required"` | ||
Message string `json:"message,omitempty"` | ||
Errors []Error `json:"errors,omitempty"` | ||
Data interface{} `json:"data,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you envision sending data with a message?
I also know that returning a top level json array is a security issue - http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ , and this would make sure that the caller never has to know about or consider this risk. |
That article appears to call out security vulnerabilities that happened in 2009, but I'd be hesitant to say anything exists like this today. GitHub, Google, GitLab and more return top-level JSON arrays and have for years: https://docs.github.com/en/rest/reference/repos |
How is httpapi.Response a unified pattern if most routes (anything that sends data) not use it? It seems we have a cognitive split when returning responses by using two different packages. If we want to continue to just return data types directly I think we need to rename httpapi.Response to be more specific to errors since that's really its only value. If we value status codes, I think we should not be returning 200s with messages like "I did the thing" because it's useless info. We should just send back an empty 200 or the updated data types. |
Every route does use it, just depends on the status code being responded. Every error gets
|
@f0ssel The cloud's have enormous APIs that demand peculiar patterns. The GCP compute API comes to mind. They have to rely on patterns like the one you proposed to scale to thousands of API endpoints in a consistent way. These abstractions are necessary but I don't enjoy interacting with these APIs. Can you provide examples of smaller, more Coder-like APIs that use something like your proposed pattern? I haven't ever worked with an API where the meat of every response was nested under a specific field. I assume most APIs avoid this nesting to save developers from one level of destructuring on every response parse. I do see the value of standard |
I actually don't care that much about the json format, but I think the real problem I have is we just have a weird mix of "generic" functions that can only be used sometimes right now. We use httpapi.Write for error and "empty " responses, but not actual data. If httpapi had a clear public api that handled the different types of responses I'd be happy. Maybe we just need httpapi.Write to take an interface{}, I think id be happy today with that. @kylecarbs what about an api like:
That seems like a pattern we could all follow implicitly I think. |
I think If it's called |
I missed your point about always sending valid JSON instead of empty responses. I'll concede that does seem important to keep standard. |
You found an interesting nit where the existing |
Ahh up to you whether we use |
Can we just return |
@coadler I'm game in theory but I do feel like always returning JSON can't hurt. Also I do think status codes are important. What if we added something like:
Usage:
|
I'd rather us just write something a lil friendly in the API. Another benefit of the message not being static is us dev-folk will never check the message content, and will always check the status code 😎 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a wise and nice improvement. Can we remove render from everywhere?
It seems a bit wasteful to me in a SaaS context to send back a bunch of data a user will never consume, but I don't feel super strongly. I really like how Discord uses http 204, which is why I bring it up. |
Ahh if Discord uses it, then that's good external precedent beyond my own judgment. I'm fine with doing that in cases where no response is needed. Sending fewer bytes is certainly the hackery thing to do 🤓! |
Good catches on the responses that weren't even being checked properly (OK instead of created)! 🧹 |
Also regarding this, I typically don't like returning raw JSON arrays because it's always a breaking change if we wanted to add extra fields, such as paging info. In V1 we always wrap array responses. |
This sounds like a good case for a custom lint rule for not passing arrays to httpapi.Write |
I think we should make that change if the time comes. Considering many APIs haven't needed to do that (eg. GitHub), I'm weary to implement it on a hunch. |
I just checked and you are right, GitHub doesn't seem to mind sending back top level arrays. Good enough for me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this.
@kylecarbs do you know what may have changed to make this happen? https://github.com/coder/coder/runs/5980847240?check_suite_focus=true |
Woah... zero clue. I'd call this a flake, but I have no idea how it'd happen. |
@f0ssel it's because of this: https://github.com/coder/coder/pull/941/files#diff-5499c2aac6d11c8e0574c18b1dd90a14bfb063426e07115d0e453adb08c33574R35 You probably shouldn't use our API package for this, because we can just |
This was a discussion that led to just making
httpapi.Write()
useful for writing json data as well as the predefined response type.