Construct query strings from FormData
- Published at
- Updated at
- Reading time
- 2min
I was wrangling some Remix TypeScript the other day and wanted to create a server action that receives submitted form data. This form data then should be used to build up a query string, make a request to a secured and hidden GET endpoint and return the result.
// what I wanted to do ...
export async function action({
request,
}) {
const formData = await request.formData();
const queryString = new URLSearchParams(formData).toString();
// make another API call with the constructed query string
const resp = await fetch(`https://hidden-api.com?${queryString}`)
// ...
}
Easy peasy, that's what URLSearchParams
is for, right? When you check MDN, you'll discover that passing FormData
into the URLSearchParams
constructor is valid. Nice!
[The URLSearchParams(options)
options object can be] a literal sequence of name-value string pairs, or any object — such as a FormData object — with an iterator that produces a sequence of string pairs.
Unfortunately, TypeScript isn't happy about passing a FormData
type into the URLSearchParams
constructor.
ts
constformData = newFormData ();constArgument of type 'FormData' is not assignable to parameter of type 'string | string[][] | Record<string, string> | URLSearchParams | undefined'. Type 'FormData' is missing the following properties from type 'URLSearchParams': size, sort2345Argument of type 'FormData' is not assignable to parameter of type 'string | string[][] | Record<string, string> | URLSearchParams | undefined'. Type 'FormData' is missing the following properties from type 'URLSearchParams': size, sortqueryString = newURLSearchParams (). formData toString ();
But why is FormData
not allowed to be used with URLSearchParams
? MDN says it should work, it's a common use case, what's up?
The problem is that FormData
could also include non-string types such as a submitted File
. As always, TypeScript is correct and rightfully complaining — the FormData
object could hold data that URLSearchParams
can't handle and this would lead to a nasty hard to find runtime exception.
So what's the solution?
I could now iterate over the form data keys and type guard them, but because I know there won't be a submitted file in my FormData
quick type casting did the trick for me.
ts
constformData = newFormData ();constsearchParams = newURLSearchParams (formData as unknown asRecord <string, string>,).toString ();
If you want to find more possible solutions, check this related GitHub issue.
Join 5.5k readers and learn something new every week with Web Weekly.