Small survey, do you have endpoints that return di...
# server
c
Small survey, do you have endpoints that return different HTTP successful status codes? For example a single endpoint that sometimes returns a
200 Ok
and sometimes a
204 No Content
, or similar. If you do, I'm interested in discussing your use-case (it impacts a design decision for spine.opensavvy.dev). (The case where an endpoint "is either successful OR fails with one of multiple error codes" is already handled)
๐Ÿ‘Œ 4
๐Ÿšซ 7
d
Absolutely. The codes are there for a reason and you can use them to infer things like when work is done (ok vs accepted) Personally I'd not recommend using any library which doesn't at least have the facility to choose if to (even if there is a default)
๐Ÿ‘ 1
c
Do you have an example of such a use-case? Spine definitely lets you choose whichever status code you want to return, but the compiler currently doesn't check that the status code the server returns corresponds to the one documented in your API spec. We're considering multiple designs to allow the compiler to verify this, so we're interested to see how people represent their APIs
d
I've previously done something similar to what you're looking to achieve with spine (it's quite easy with what we've got in http4k). The challenges are around exactly this type of thing , along with building in expand-contract and versioning
h
I have a similar framework to generate the OpenAPI file from code and have similar challenges, so I only support compile time constants. If you can, you should write your api spec using OpenAPI and generate your code instead of try to evaluate the source code.
c
Guys could you please answer the question ๐Ÿ˜…
๐Ÿ˜… 1
h
Hm? Of course I do use 202 or 204
c
Can you give me a real-world example endpoint that uses both?
If it's a proprietary API, don't hesitate to rename some things around, I'm interested in how the status codes are interpreted client-side, not necessarily the exact naming
d
Do you have an example of such a use-case?
For 200 Vs 202 on an update, you're trying to convey that the difference between work done and work queued. It can have a semantic difference on how the client behaves
h
I would only implement it, if your users are requesting it and not implement any use case from random users.
d
I'd lean towards a sealed result type which conveys the 2 cases using the type system
๐Ÿ‘Œ 1
h
But queuing is my use-case too and I also use a result type.
c
For 200 Vs 202 on an update, you're trying to convey that the difference between work done and work queued.
Do you have such an endpoint in an API that uses both? I know that's theoretically possible, but I've never seen an API that sometimes enqueues and sometimes does immediately
h
Sure, if the result is already computed/processed
c
I'd lean towards a sealed result type which conveys the 2 cases using the type system
Does the client switch on the result type (after deserialization of the body), or do they switch on the status code itself (before deserialization of the body)?
h
And we have an internal api that returns 204 in case of no results when doing a search.
๐Ÿ‘€ 1
d
Or if the result would be stored but cannot be because some downstream system is unavailable and therefore you "inbox" the work instead
c
And we have an internal api that returns 204 in case of no results when doing a search.
That's the kind of examples I'm searching for, thanks
d
Previously we'd detect the response type and then convert to the sealed type (with body) on that basis.
h
> That's the kind of examples I'm searching for, thanks Well, I would not recommend it and not expect a framework handling this use case.
c
Previously we'd detect the response type and then convert to the sealed type (with body) on that basis.
Do you have an example where the DTO is completely different depending on the status code (ignoring error status codes), and your serialization library would not be able to deserialize the general case? As a counter-example: if the DTO includes a
type: foo
discriminator that is redundant with the status code, the deserialization library can it use to switch on even if the status code is unknown
d
It's rarer but I have seen it. OpenAPI accounts for this case so I'd expect it tbh. It seems to be popular with the AI providers where the shapes can be totally different without a type field
โž• 1
Although in that AI case its not at the top level.
c
It seems to be popular with the AI providers where the shapes can be totally different without a type field
Can you give me a specific example? A link to their documentation would be perfect
d
https://www.openresponses.org/ I was looking at implementing this for http4k yesterday and the bot reported that some shapes were different and didn't have a type discriminator. But once again - these weren't too level shapes but subobjects
r
hey @hfhbd, you mention you got a framework for that... do you have a link or is it not FOSS? Tight ktor-server-client connetion is a topic I'm very interested.
c
Tight ktor-server-client connetion is a topic I'm very interested.
Please read spine.opensavvy.dev, that's exactly what I'm doing ๐Ÿ™‚
h
I have an OpenAPI generator that supports Ktor client and Ktor server: https://github.com/hfhbd/kfx But you can use any other generator too. I really recommend to write your API as contract instead of evaluating source code. I do also have a compiler plugin, that reads the source code and generates the OpenAPI, but it only supports a specific framework: the SAP Integration Suite https://github.com/softwork-app/cikraft
r
to respond to @CLOVIS, I don't do that cause for testability purposes I isolate HTTP from the components that process the data. In code it looks something like
getUsers: suspend () -> Result<GetUsersResponse>
. So it hides the HTTP parts away and simplifies a lot unit testing, hence it either success of an expcetion for Controller/ViewModel
c
Spine has type-safe compile-time code-first error management built-in, with no outside build tool modification, no code generator and no compiler plugin. I don't think a single other project can do that
In code it looks something like
getUsers: suspend () -> Result<GetUsersResponse>
. So it hides the HTTP parts away and simplifies a lot unit testing, hence it either success of an expcetion for Controller/ViewModel
Yeah, all fullstack projects I've seen so far don't bother with the status code at all, and just use sealed hierarchies inside the response body to represent the different cases. That's trivially supported by any framework, but I'm interested in the more complex use-cases, since they are theoretically possible in the spec
(also Spine is not limited to Ktor, and I'll probably make an Http4K adapter at some point, but it's not planned for the near future)
r
trivially supported by any framework
not supported on the openApi generator thingy =/
but I'm interested in the more complex use-cases, since they are theoretically possible in the spec
definitely, from a library perspective makes total sense.
that's exactly what I'm doing
I guess that's exactly what we are all doing ๐Ÿ˜„ github.com/budius/kebab-krafter, but I went with the Swagger -> Code generator approach
h
trivially supported by any framework
not supported on the openApi generator thingy =/
Why not? It is really trivial.
r
I don't know why, years ago when I started the project we looked into it and it just couldn't support `anyOf`/`oneOf` for KotlinX serialization
to be clear, I'm talking about this here swagger.io/open-source/swagger-codegen (or maybe it was this openapi-generator.tech/docs/swagger-codegen-migration it has been a few years) not generating kotlinx Serialization
h
Yeah, anyOf/oneOf is challenging yes, because JSON schema is a runtime constraint system and Kotlin with data classes requires compile time inheritance. But different status codes are not challenging.
d
There's also this coming down the pipe... https://openapispec.com/docs/what/what-is-openapi-moonwalk/
๐Ÿ‘€ 2
r
anyOf/oneOf is challenging yes
... and ultimately led me to develop the link I sent, but I agree it was hard to map anyOf/oneOf to sealed classes.
a
Yes. Mainly upserts (
PUT
returning
201
on create,
200
on update) and idempotent `POST`s (
201
first time,
200
on replay with the same key). Also conditional GETs (
200
vs
304
) and async endpoints that return
202
when queued but
200
if they finished inline. In all of these the status code tells the client something the body doesnโ€™t, so itโ€™d be nice if Spine could model them as different success variants.
โž• 1
๐Ÿ‘€ 1
h
> anyOf/oneOf is challenging yes > ... and ultimately led me to develop the link I sent, but I agree it was hard to map anyOf/oneOf to sealed classes. It depends on the actual JSON schema. It does work well if you use references and very easy and just a simple relationship, but it is challenging when using inline schemas. Or multi level relationships.
๐Ÿ‘ 1
d
Yes, we have endpoints which have different status codes, depending on specific cases. Like we have some data and the calculation based on them deliver 204, 200 or 404. We use http4k http4k and result4k to model different cases.
c
In all of these the status code tells the client something the body doesnโ€™t, so itโ€™d be nice if Spine could model them as different success variants.
What does the client do differently based on this information? Or is it just used for debugging?
Like we have some data and the calculation based on them deliver 204, 200 or 404.
Can you give a specific example and explain what you're doing different based on the status code?
m
a combo: POST endpoint returns 201/202 depending on the underlying processing pipeline - short, instant 201, longer 202 - both statuses have same return, a pointer to where the resulting resource is located the endpoint that was pointed to will return 204, to signal - resource exists, but for now, no content - as soon as pipeline finishes, it will do 200
๐Ÿ‘ 2
c
๐Ÿ‘๐Ÿผ 1