fermyon/spin-js-sdk

Unable to access request body when using the router

radu-matei opened this issue · 7 comments

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.

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.

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

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

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.

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

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);
};

@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

7ff233a

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.