Home

Starting the API

17/01/2023

It is time to start writing the API for the application, so that when I tackle the UI there is something to integrate with. I've sketched some potential API routes already, such as:

/structure

/new

This route will load the structure.json file if it exists.

I'm trialling a Kotlin library I found on github which provides a routing interface for AWS Lambda functions, called, simply enough lambda-kotlin-request-router. It will allow me to write the examples above as something like:

    override val router = router {
    GET("/structure") { r: Request<String> ->
        val structureJson = s3client.get(//....)
        ResponseEntity.ok(Structure(structureJson)))
    }

    POST("/new") { r: Request<MyRequest> ->
        ResponseEntity.created(body = MyResponse("Created " + r.body.message))
    }
}

I'm struggling a bit though, trying to test it through the complicated AWS API Gateway Proxy configuration that I need. I am also struggling to get any sort of meaningful logging done, and splitting the lambda into separate 'controller' classes is not going well at all. Just returning generic server errors. For logging, I have had to revert to println statements, and I cannot get the separate controllers working at all.

The library I am using hasn't been updated for a couple of years, and I suspect it was built for some company's internal project so it may meet their needs sufficiently. I'm half-considering forking it, but I'll look for alternatives first.

Scuppered

It's pretty clear that the library no longer supports the useful class::method syntax for declaring route handling functions. Indeed, a unit test has been written explicitly stating that this doesn't work, blaming a possible bug in Kotlin's reflection code: Throw exception when reflection fails. Even Jetbrains has an open bug about it.

This is unfortunate because it forces me to put all route implementations inside the anonymous lambda, and can't put it in a separate class and function. This is despite it being an advertised feature of the library. Being able to write:

    override val router = router {
    GET("/post/{id}", postsController::get)
    POST("/new", postsController::new)
    DELETE("post/{id}", postsController::delete)
}

And putting all the implementation details into a separate PostsController class is much nicer than having to write something like:

override val router = router {
    GET("/post/{id}") { req: Request<NewPostRequest> -> 
         val s3Client = S3Client.builder()
                .region(Region.EU_WEST_2)
                .build()
        s3Client.get(GetObjectRequest.builder().key(.....))
        // etc etc
    }
    POST("/new") { req: Request<NewPostRequest> ->
        val s3Client = S3Client.builder()
            .region(Region.EU_WEST_2)
            .build()
        s3Client.put(PutObjectRequest.builder().key(.....))
        // etc etc}
    }
    // etc etc
}

This will make for a very long and hard-to-read routing function.

Prev: One small bug, two days of pain Next: Forking a library