# Multipart upload worked example?

**URL:** <https://softwaremill.community/t/multipart-upload-worked-example/118>\
**Category:** tapir\
**Created:** [February 6, 2023, 6:30pm UTC](https://softwaremill.community/t/multipart-upload-worked-example/118 "2023-02-06T18:30:25Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jhenahan](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/jhenahan/32/91_2.png) [@jhenahan](https://softwaremill.community/u/jhenahan)\
**Post date:** [February 6, 2023, 6:30pm UTC](https://softwaremill.community/t/multipart-upload-worked-example/118/1 "2023-02-06T18:30:25Z")

</div>

Anyone have a worked example of properly setting up an endpoint that accepts a multipart-data body? I’m consistently getting `Invalid value for: body (expected digit got '--0db4...' (line 1, column 1))` My current application is stuck on 0.19, but I’ve spiked a 1.x upgrade with the same result. I’ve also tried with both the Akka-HTTP backend and the HTTP4S backend with the same result.

```plaintext
import io.circe.Json
import sttp.model.{Part, StatusCode}
import sttp.tapir._
import sttp.tapir.json.circe._

import java.io.File
import sttp.tapir.generic.auto._
import io.circe.generic.auto._

case class MultipartData(operations: Json, file: Part[File])

object Endpoints {
  val uploadEndpoint
    : PublicEndpoint[MultipartData, Unit, Json, Any] =
    endpoint.post
      .in("upload")
      .in(multipartBody[MultipartData])
      .out(jsonBody[Json])
}

```

The requests I’m sending to this endpoint look like this

```auto
POST /upload HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1
Host: localhost:8080

--13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1
Content-Disposition: form-data; name="file"; filename="Test_Upload.csv"
Content-Type: text/csv

... CSV Data ...

--13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1
Content-Disposition: form-data; name="operations"

... Some text ...

--13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1--

```

And the response is

```auto
HTTP/1.1 400 Bad Request
Content-Length: 75
Content-Type: text/plain; charset=UTF-8
Date: Mon, 06 Feb 2023 18:24:01 GMT
Server: akka-http/10.2.6

Invalid value for: body (expected digit got '--13d6...' (line 1, column 1))

```

---

<div class="post-metadata">

**Author:** ![adamw](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/adamw/32/28_2.png) [@adamw](https://softwaremill.community/u/adamw)\
**Post date:** [February 7, 2023, 1:28pm UTC](https://softwaremill.community/t/multipart-upload-worked-example/118/2 "2023-02-07T13:28:05Z")

</div>

Unfortunately I can’t reproduce this. My test server is as follows:

```plaintext
package sttp.tapir.examples

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import sttp.model.Part
import sttp.tapir._
import sttp.tapir.generic.auto._
import sttp.tapir.server.akkahttp.AkkaHttpServerInterpreter

import java.io.File
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}

case class MultipartData(operations: String, file: Part[File])

object X extends App {
  val uploadEndpoint: PublicEndpoint[MultipartData, Unit, Unit, Any] =
    endpoint.post
      .in("upload")
      .in(multipartBody[MultipartData])

  //

  implicit val actorSystem: ActorSystem = ActorSystem()
  import actorSystem.dispatcher

  // starting the server
  val routes = AkkaHttpServerInterpreter().toRoute(List(uploadEndpoint.serverLogicPure[Future] { r =>
    println(r)
    Right(())
  }))

  val bindAndCheck = Http().newServerAt("localhost", 8080).bindFlow(routes).map { _ =>
    println("Press any key to exit ...")
    scala.io.StdIn.readLine()
  }

  // cleanup
  Await.result(bindAndCheck.transformWith { r => actorSystem.terminate().transform(_ => r) }, Duration.Inf)
}

```

I’m sending the request as you gave it, from Idea’s HTTP client:

```auto
POST /upload HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1
Host: localhost:8080

--13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1
Content-Disposition: form-data; name="file"; filename="Test_Upload.csv"
Content-Type: text/csv

... CSV Data ...

--13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1
Content-Disposition: form-data; name="operations"

... Some text ...

--13d6528ff0084c82-7962ea0aa0fb1db2-44e7437e0695a499-ad43feb4ebb453c1--

```

And I’m getting a 200 OK, on the backend the println is as follows:

```auto
MultipartData(... Some text ...,Part(file,/var/folders/99/1jlks6pn7gz_7mdlt4n3hkbm0000gn/T/tapir6671885412493013947tmp,TreeMap(filename -> Test_Upload.csv),List(Content-Transfer-Encoding: binary, Content-Type: text/csv)))

```

So there must be sth different in your setup which causes the issue to surface

---

<div class="post-metadata">

**Author:** ![jhenahan](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/jhenahan/32/91_2.png) [@jhenahan](https://softwaremill.community/u/jhenahan)\
**Post date:** [February 7, 2023, 4:51pm UTC](https://softwaremill.community/t/multipart-upload-worked-example/118/3 "2023-02-07T16:51:23Z")

</div>

Thank you so much for taking the time to (attempt to) reproduce. It helped me locate the source of my problem. I thought the issue was in the endpoint definition itself, but it was actually in the route definition where I was stitching all my endpoints together.

The real issue was that my real application has a second `endpoint.post` with no path qualification, and in my `toRoutes` call I had that route listed before the `POST /upload`, which I take to mean when the route matcher is constructed my upload calls were being fed to the unqualified endpoint and never reaching the upload route, at all. Reordering the endpoint list resolved the issue.

I’ll be much more careful about that and use `.endpoint("")` in the future. 😃 Thanks again.

---

<div class="post-metadata">

**Author:** ![adamw](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/adamw/32/28_2.png) [@adamw](https://softwaremill.community/u/adamw)\
**Post date:** [February 7, 2023, 7:17pm UTC](https://softwaremill.community/t/multipart-upload-worked-example/118/4 "2023-02-07T19:17:50Z")

</div>

Great to know that it’s working 🙂

Adding a test for shadowed endpoints might help detect such situations: [Testing — tapir 1.x documentation](https://tapir.softwaremill.com/en/latest/testing.html#shadowed-endpoints)

---

<div class="post-metadata">

**Author:** ![jhenahan](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/jhenahan/32/91_2.png) [@jhenahan](https://softwaremill.community/u/jhenahan)\
**Post date:** [February 7, 2023, 8:14pm UTC](https://softwaremill.community/t/multipart-upload-worked-example/118/5 "2023-02-07T20:14:57Z")

</div>

Oddly enough, I _have_ a test for shadowed endpoints for exactly that reason, but it didn’t detect this particular overlap. I’ll see if I can figure out why that is.

---

<div class="post-metadata">

**Author:** ![adamw](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/adamw/32/28_2.png) [@adamw](https://softwaremill.community/u/adamw)\
**Post date:** [February 8, 2023, 8:00am UTC](https://softwaremill.community/t/multipart-upload-worked-example/118/6 "2023-02-08T08:00:40Z")

</div>

Hm that’s weird - if you would be able to minimize the issue, please let us know 🙂
