Multipart Form-data Standardization

Multipart form-data lacks a standardized way to handle arrays and nested objects, while many APIs require this feature. Over the past years, many companies and developers have proposed different solutions, but these often diverge significantly and require extra effort to adopt all of them. This blog post aims to analyze existing approaches and identify one approach that is scalable, straightforward, and production-ready for parsing nested objects and arrays in multipart form-data, which should have already been widely adopted by major companies.

Approach 1: Repeated Key Names (⛔)

This approach uses repeated key names to represent arrays. For example:

JSON Input

{ "name": "John", "hobbies": ["reading", "coding", "traveling"] }

Multipart Form-Data

name=John
hobbies=reading
hobbies=coding
hobbies=traveling

While initially straightforward, this method fails when handling nested structures. For instance:

address[street]=123 Main St
address[street]=456 Main St
address[street]=789 Main St

Could be interpreted as:

Decoded Option 1

{ "address": { "street": ["123 Main St", "456 Main St", "789 Main St"] } }

Decoded Option 2

{ "address": [ { "street": "123 Main St" }, { "street": "456 Main St" }, { "street": "789 Main St" } ] }

This ambiguity makes the approach unsuitable for scalable APIs with complex data structures. This method is documented in Swagger's multiple file upload example (reference).

Approach 2: JSON Encoding (⛔)

This approach embeds JSON strings into form-data:

JSON Input

{ "name": "John", "hobbies": ["reading", "coding", "traveling"] }

Multipart Form-Data

name=John
hobbies=["reading", "coding", "traveling"]

Although intuitive, this method is rarely supported natively by frameworks and adds overhead for parsing and validation. Form-data is designed for key-value pairs, making this approach impractical for production environments.

MDN encourages using repeated keys (reference), thus JSON encoding approach should be least considered.

Approach 3: Repeated Keys with Brackets (✅)

This widely adopted approach uses square brackets ([]) to represent arrays. For example:

JSON Input

{ "name": "John", "hobbies": ["reading", "coding", "traveling"] }

Multipart Form-Data

name=John
hobbies[]=reading
hobbies[]=coding
hobbies[]=traveling

For nested objects, brackets allow clear and unambiguous representation:

JSON Input

{ "name": "John", "address": { "street": ["123 Main St", "456 Main St", "789 Main St"] } }

Multipart Form-Data

name=John
address[street][]=123 Main St
address[street][]=456 Main St
address[street][]=789 Main St

When representing arrays of objects:

JSON Input

{ "address": [ { "street": "123 Main St" }, { "street": "456 Main St" }, { "street": "789 Main St" } ] }

Multipart Form-Data

address[][street]=123 Main St
address[][street]=456 Main St
address[][street]=789 Main St

This method ensures clarity and scalability, making it the most suitable choice for production use.

In Production Examples

  • OpenAI has already used this approach in one of their multipart form-data endpoints (reference).
  • Stainless API supports this (and only this) behavior in all their generated clients, including ones for OpenAI, Anthropic, Groq, and more.
  • Axios (TypeScript) supports this behavior in their official documentation (reference).

Approach 4: Repeated Keys with Brackets and Index (✅)

This approach uses repeated keys with brackets and an index to represent arrays. For example:

JSON Input

{ "name": "John", "hobbies": ["reading", "coding", "traveling"] }

Multipart Form-Data

name=John
hobbies[0]=reading
hobbies[1]=coding
hobbies[2]=traveling

This approach is similar to Approach 3, but it adds an index to the key, so it can prevent ambiguity and cases where repeated keys are not allowed (e.g. Python dict). In addition, AWS does not recommend having repeated keys in multipart uploads (reference), so this could be a great alternative in such cases. Overall, this approach is also scalable and production-ready. Fiber (in Golang) now supports this behavior (reference).

Alternative Ideas

  • Gemini Audio: Use separate endpoints for handling file uploads (so no need to send nested objects / arrays along with the form).
  • OpenAPI 3.1 now supports encoding objects as part of multipart form-data.

Conclusion

Among the approaches discussed, Repeated Keys with Brackets stands out as the most scalable, production-ready solution for handling arrays and nested objects in multipart form-data. It is widely adopted and avoids the pitfalls of ambiguity and excessive overhead, making it a reliable standard for modern APIs.

January 10, 2025