gonest
Multipart & File Uploads

Multipart Form Streaming

True streaming file uploads, no buffering

Most frameworks (including NestJS's own multer/FileInterceptor) buffer an uploaded file to memory or disk before handler code runs. gonest streams instead: onFile fires the instant a file part is seen in the raw multipart stream, letting the handler forward bytes directly to e.g. S3 without ever buffering the whole file.

type CreatePostForm struct {
  Title string `form:"title"`
}

var createPostFormSchema = gonest.NewSchema[CreatePostForm](func(t *CreatePostForm, m *gonest.Schema) {
  m.Property(&t.Title).String().Required()
})

var PostController = gonest.NewController(func(controller *gonest.Controller) {
  controller.RoutePost("/", func(route *gonest.Route) {
    route.FormBody(createPostFormSchema, "file") // documents multipart/form-data in OpenAPI
    route.Handler(func(ctx *gonest.RestContext) {
      form := gonest.MustParseRestFormBody[*CreatePostForm](ctx, createPostFormSchema, func(f *gonest.FormFile) error {
        return uploadToS3(f.Filename(), f.ContentType(), f.Reader())
      })
      ctx.Json(map[string]string{"title": form.Title})
    })
  })
})

func main() {
  app := gonest.MustNewApp[gonest.FiberApp](AppModule, gonest.AppOptions{EnableFormStreaming: true})
  app.MustListen(":3000")
}

FormFile

The callback passed to MustParseRestFormBody receives a *gonest.FormFile with Filename(), ContentType(), and Reader().

AppOptions.EnableFormStreaming

This flag is app-wide, not per-route — it toggles Fiber's StreamRequestBody + DisablePreParseMultipartForm together. Existing JSON/param/query routes are unaffected.

New tag family: form:"...". The same Custom(fn) escape hatch works here too, receiving the raw string value.

Trade-off, not a bug: if onFile returns a non-nil error, the walk stops and produces a *BadRequestException including the field name and the callback's own error message. But if a later form field turns out to be invalid, onFile may have already fired for files seen earlier in the stream — design your own rollback (e.g. delete a partially-uploaded S3 object) for "upload started, but the request turned out invalid."

Next steps

On this page