Emily
05/09/2025, 1:49 PMString.replace("\n", "")
. This silently breaks with template files using CRLF. A simple solution could be to change it to String.replace("\r\n", "").replace("\n", "")
.dave
05/09/2025, 4:44 PMNathan Hartwell
05/14/2025, 3:59 PMRequest(Method.POST, "/echoJson").body("""{"name": "Test"}""")
) returns this rather generic {"message":"Missing/invalid parameters","params":[{"name":"body","type":"body","datatype":"object","required":true,"reason":"Invalid"}]}
. Is there an easy (or hard) way to get a more detailed error message? I tried adding a CatchLensFilter, but it looks like the contract renderer(?) already catches and handles that.
the closest I've found to an answer is this 2023 slack threaddave
05/18/2025, 6:35 PMCies
05/20/2025, 9:13 AMdave
05/20/2025, 7:33 PMleonhardt
05/21/2025, 8:01 PMArnab
05/26/2025, 8:15 AMKristina
05/28/2025, 10:16 AMGopal S Akshintala
06/01/2025, 8:07 AMdmcg
06/02/2025, 9:50 AMval itemIds = request.form().map<Parameter, String> { it.first }
.mapNotNull<String, ID<Item>> { ID<Item>(it) }
.toSet<ID<Item>>()
I have sometimes seen warnings, or maybe errors, that Parameter
isn’t visible, but it has always compiled eventually. Upgrading Kotlin to 2.1.20 it now won’t compile at all, although, it has to be said, the type parameters aren’t now required (I don’t know if or why they were previously).
Asking here in case anyone else has this issue. Was Parameter
ever non-internal? If not does anyone know how this code compiled in the past?Cies
06/02/2025, 2:53 PMwww-form-urlencoded
POST body received by a http4k server to a data class
, preferably with kotlinx.serialization
. I've done this before (when not using http4k) with Jackson, using quite some self written code, to (1) create a little language for adding structure to the form's name
values (this allowed me to have nested data structures, and list/arrays, like .user.permissions[5]
) and to parse the data directly to Jackson's internal JSON representation (in order to skip a JSON format step: Jackson was mainly used for it's "type coercion" since all values are received in one big www-form-urlencoded
string).
Is there anything in http4k to make this easier? I've seen the body lens feature, but I have not been able to use it for form submissions.andyg
06/09/2025, 2:50 AMCies
06/10/2025, 11:37 PMauthenticated
role, you simply need the service_role
for writes); this to keep RLS dead simple and still useful (as an extra safety against data leaks and to allow Supabase goodness like GraphQL and realtime to be used later on). Both JWT-from-cookie and db-connection-with-supabase-auth are implemented as http4k `Filter`s.
The idea is: start SSR, because it is just waaay simpler, then add little browser apps on top later where it adds maximum value (and for that Supabase is really nice with GraphQL and realtime).
It used kotlinx.html for template rendering (with a layouting system) and Konform for FormDto validation.
Jdbi was chosen as I want to be able to write "just SQL". I might one day convert it to terpal-sql (the basis of ExoQuery) but it did not work out of the box, so I went with time-tested Jdbi.
Feel free to AMA, and if you have a look at the code and see any room for improvement: please let me know! I'm really hungry for feedback.
https://github.com/cies/supabase-http4k-ssr-jdbiDanielZ
06/11/2025, 9:24 AMhelidon
as server. In my service I have multiple calls to upstream services (using okhttp
).
But during deployment in k8s I'm facing that helidon
will kill all running requests (Closed by interrupt
) even when I sleep first in my shutdown hook.
So far I understand helidon
has it's own shutdown hook registered by default. Would be nice to support in addition graceful stopmode to overcome that kind of issue.Cies
06/12/2025, 8:40 AMhs256
algorithm that Supabase uses?Cies
06/12/2025, 8:42 AMPaul Reijbroek
06/13/2025, 6:52 PMDanielZ
06/13/2025, 8:23 PMconst val USE_REAL_CLIENT = false
fun main() {
val env = Environment.defaults(
AWS_REGION of Region.EU_WEST_1,
AWS_ROLE_ARN of ARN.of("arn:aws:sts:us-east-1:000000000001:role:myrole")
)
val http: HttpHandler = if (USE_REAL_CLIENT) JavaHttpClient() else FakeS3().debug()
val credentialsProvider = CredentialsProvider.STS(env, FakeSTS().debug())
val s3 = S3.Http(credentialsProvider = credentialsProvider, http)
val sourceName = BucketName.of("source-bucket")
val targetName = BucketName.of("target-bucket")
s3.createBucket(sourceName, env[AWS_REGION]).recover(RemoteFailure::throwIt) // <<< fails here: env 'AWS_ACCESS_KEY_ID' is required
s3.createBucket(targetName, env[AWS_REGION]).recover(RemoteFailure::throwIt)
val source = S3Bucket.Http(sourceName, env[AWS_REGION], credentialsProvider, http)
source.putObject(BucketKey.of("hello"), "hello ".byteInputStream()).recover(RemoteFailure::throwIt)
val target = S3Bucket.Http(targetName, env[AWS_REGION], credentialsProvider, http)
target.copyObject(source.bucketName, BucketKey.of("hello"), BucketKey.of("copy"))
.recover(RemoteFailure::throwIt)
}
Tudor Luca
06/14/2025, 7:14 PMdave
06/18/2025, 12:27 PMdave
06/20/2025, 9:10 PMleonhardt
06/30/2025, 4:02 AMsinglePageApp
helper be used on a subpath? When it's configured on the root path, it works as expected. But when it's configured on a subpath, it still seems to match any path, even ones outside the subpath.Marco Garofalo
06/30/2025, 3:42 PM<http://whatever/me>
) or propagate the token down the handlers chain/stack once the authFilter is satisfied.leonhardt
07/12/2025, 5:38 PMorg.http4k:http4k-testing-approval
in conjunction with your Okey-doke plugin for a couple months now. The tool + plugin has definitely a huge improvement over renaming the generated files manually, but now that we've been living with the process for a while a few DX issues have surfaced:
1. It's hard to know if the keyboard shortcut (Ctrl + F11) worked. You have to have the cursor focused in the right place. Precise placement in the editor on the test or in the project viewer on the file both work, but (perhaps unintuitively) the test execution's run window/console does not work. And there's no visual indication if it worked, beyond navigating to the file in the project viewer. As a result I've noticed we just hit Ctrl + F11 obsessively in every window "just to make sure."
2. When we rename tests, the generated approval files get abandoned. We often don't notice initially, and only after some time notice when there's many more approval files than tests. The process to clean up the clutter is tricky. We usually make sure all our tests are passing, commit, delete all the approval files, rerun tests, approve all the approval files, and commit again. But this is a little error-prone and tedious.
When we only had 1-20 tests, these weren't big issues. But now that we're in the 100s it's painful enough issue we're looking to address it. Thought we'd check with the community here. Can anybody recommend any better ways to work with org.http4k:http4k-testing-approval
that we might have missed?ted
07/28/2025, 11:29 AMCies Breijs
08/07/2025, 3:42 AM@RequiredPermissions(Perm.SEE_GOD, Perm.HEAR_GOD, Perm.FEEL_LOVE) // or: @RequiresNoPermissions
fun ultimateExperienceGetHandler(req: Request): Resposne { /* ... */ }
The were serialized to the db's user_permissions
table (with columns: userId, permEnum). Kind of worked well.
I liked that all handlers required such annotation (created some test for that in the test suite).
It feels like in http4k the best place to implement this is simply in the handler body. Functional. No annofuzz...
(But please correct me if I'm wrong about this)
fun ultimateExperienceGetHandler(req: Request): Resposne {
if (!currentUserCtx(req).permissions.contains(Perm.SEE_GOD)) return Response(FORBIDDEN)
// ...
}
But there's one thing I miss: the "closed by default" that I had with the annotations.
So I wondered, would it be possible to have some sort of "closed by default" back by somehow specifying them in the router...
Then:
val portalRouter = routes(
Paths.dashboard bind GET to ::dashboardGetHandler,
Paths.profileEdit bind POST to ::profilePostHandler,
Paths.profileEdit bind GET to ::profileGetHandler,
Paths.retailerList bind GET to ::retailerListGetHandler,
Paths.retailerView bind GET to ::retailerViewGetHandler,
// ...
)
Would become something like:
val portalRouter = authorizedRoutes(
Triple(Paths.dashboard, GET to ::dashboardGetHandler, listOf(Perm.SEE_DASHBOARD)),
Triple(Paths.profileEdit, POST to ::profilePostHandler, listOf(Perm.MODIFY_MY_PROFILE)),
Triple(Paths.profileEdit, GET to ::profileGetHandler, listOf(Perm.MODIFY_MY_PROFILE)),
// ...
)
Or even methods on the UserPermissions object like:
val portalRouter = authorizedRoutes(
Triple(Paths.dashboard, GET to ::dashboardGetHandler, listOf(::seeDashboard)),
Triple(Paths.profileEdit, POST to ::profilePostHandler, listOf(::modifyMyProfile)),
Triple(Paths.profileEdit, GET to ::profileGetHandler, listOf(::userHasOrganizationWithTypeX)), // <--
// ...
)
Maybe my question is: how are y'all implementing authorization? (give that i need a very simple scheme for the foreseeable future)Cies Breijs
08/08/2025, 10:29 AMUriTemplate
to build paths in a slightly more typesafe way.
When looking at it's implementation I kind of wish for there to be a version of this function that throws an error if not all parameters are filled. Like generateOrThrow
. This may help in some cases where you forget to pass a parameter (and I cannot forsee any situation where you do not want a parameter filled in).
Am I on to something, or is this is stupid idea?
Here the original function:Slackbot
08/14/2025, 2:10 PM