fermyon / spin-js-sdk

https://developer.fermyon.com/spin/javascript-components
Apache License 2.0
54 stars 18 forks source link

Unable to access request body when using the router #132

Open radu-matei opened 1 year ago

radu-matei commented 1 year ago

I seem to be unable to access the request body when using the router. If I parse the body in handleRequest (the commented line), I can access it and everything works well. When I pass the request to the router, I can't access the body anymore.

For example:

router.post("/api/test", async req => {
    console.log(JSON.stringify(req));
    console.log(new TextDecoder().decode(req.body));
});

export async function handleRequest(request: HttpRequest): Promise<HttpResponse> {
    // console.log(new TextDecoder().decode(request.body));
    return await router.handleRequest(request);
}

This results in:

{"method":"POST","url":"http://localhost:3001/api/test","query":{},"params":{}}
Handler returned an error: Uncaught TypeError: cannot read property 'buffer' of undefined

I am able to pass it as an extra argument:

    return await router.handle({
        method: request.method, url: request.uri}, { body: request.body });

However, I think it should be part of the request object passed by default.

karthik2804 commented 1 year ago

This is because of how itty-router works. The first parameter passed to itty-router is of type RequestLike which does not contain a body field (only method & url). https://github.com/kwhitley/itty-router/blob/ab002f2e43964874ad60e91e8610c76c4c347268/src/itty-router.ts#L5

The first parameter passed to the route handling function is of type IRequest which does not contain the body field either. https://github.com/kwhitley/itty-router/blob/v3.x/src/itty-router.ts#L5

I will look into this to see if there are any easy ways of bypassing this issue.

radu-matei commented 1 year ago

Ok, that is good to know. Ideally, we want to document this behavior and make it clear how it should be used.

bacongobbler commented 1 year ago

I just hit a similar issue while migrating over to the new JS router. Thanks @radu-matei for the workaround.

It appears there's some middleware in itty-router-extras that will allow users to safely parse and embed request bodies. Is that something we can integrate with here?

https://www.npmjs.com/package/itty-router-extras#withcontent

karthik2804 commented 1 year ago

The last time I looked at it, It did not run properly due to missing functions in the runtime I think. I will take a look again and see if it is a quick fix.

bacongobbler commented 1 year ago

Yeah, I just tested this locally by attempting to bypass spin's builtin router and use itty-router directly. You get a stack trace as soon as you call req.json().

code snippet:

import { HandleRequest, HttpRequest, HttpResponse } from "@fermyon/spin-sdk";
import { Router } from "itty-router";

const router = Router();

router.post("/", async req => {
    console.log("Parsing body");
    let content = req.json();
    return {
        status: 200,
        body: JSON.stringify(content)
    }
}

export const handleRequest: HandleRequest = async function (request: HttpRequest): Promise<HttpResponse> {
    return await router.handle({
        url: request.uri,
        method: request.method
    });
};

Log output from a POST request:

Parsing body
Handler returned an error: Uncaught TypeError: not a function
bacongobbler commented 1 year ago

For others, here's a full example showing how to read the request body using the Spin router:

import { HandleRequest, HttpRequest, HttpResponse } from "@fermyon/spin-sdk";

const router = utils.Router();
let decoder = new TextDecoder();

router.post("/", async (_, body) => {
    let message = decoder.decode(body);

    return {
        status: 200,
        body: message,
    };
});

export const handleRequest: HandleRequest = async function (request: HttpRequest): Promise<HttpResponse> {
    return await router.handleRequest(request, request.body);
};
karthik2804 commented 1 year ago

@bacongobbler, The error you ran into might be due to the function not existing on the body. There was a bug with the typing fixed by the following

https://github.com/fermyon/spin-js-sdk/commit/7ff233a81145dc03815b5b3333b89e73aa1fd3ed

But excluding that, I also notice that you are not using the itty-router-extras middleware. itty-router by default does not take in a body. The first example you tried also seems broken because the entire request is not even being passed.