Streaming & Raw Body

GoNest provides utilities for streaming file downloads and accessing the raw request body.

StreamableFile

StreamableFile wraps an io.Reader to stream file downloads with proper headers. Equivalent to NestJS StreamableFile.

From an io.Reader

func (c *Controller) download(ctx gonest.Context) error {
    file, err := os.Open("reports/annual.pdf")
    if err != nil {
        return gonest.NewNotFoundException("file not found")
    }

    sf := gonest.NewStreamableFile(file,
        gonest.WithFileName("annual-report.pdf"),
        gonest.WithContentType("application/pdf"),
        gonest.WithLength(fileInfo.Size()),
    )
    return sf.Send(ctx)
}

From Bytes

func (c *Controller) export(ctx gonest.Context) error {
    data := generateCSV()

    sf := gonest.NewStreamableFileFromBytes(data,
        gonest.WithFileName("export.csv"),
        gonest.WithContentType("text/csv"),
    )
    return sf.Send(ctx)
}

Options

OptionDescription
WithContentType(ct)Set the Content-Type header. Default: application/octet-stream
WithFileName(name)Set download filename and Content-Disposition: attachment
WithDisposition(d)Set Content-Disposition directly
WithLength(n)Set Content-Length header

Content type is auto-detected from the file extension when using WithFileName and no explicit content type is set.

StreamableFile API

MethodDescription
Send(ctx)Stream the file to the response
GetContentType()Returns the configured content type
GetDisposition()Returns the configured content disposition
GetLength()Returns the configured content length

The reader is automatically closed after streaming if it implements io.Closer.


Raw Body

Access the raw request body as []byte. The body is cached on first read so it can be accessed multiple times alongside Bind(). Equivalent to NestJS @RawBody().

Direct Usage

func (c *Controller) webhook(ctx gonest.Context) error {
    body, err := gonest.RawBody(ctx)
    if err != nil {
        return err
    }

    // Verify webhook signature using raw bytes
    if !verifySignature(body, ctx.Header("X-Signature")) {
        return gonest.NewUnauthorizedException("invalid signature")
    }

    // Bind() still works after RawBody()
    var payload WebhookPayload
    if err := ctx.Bind(&payload); err != nil {
        return err
    }

    return ctx.NoContent(http.StatusOK)
}

RawBodyMiddleware

For routes where you always need raw body access, apply the middleware to pre-read and cache the body:

// Global
app.UseGlobalMiddleware(gonest.NewRawBodyMiddleware())

// Or per-route via middleware consumer
consumer.Apply(gonest.NewRawBodyMiddleware()).ForRoutes("/webhooks/*")

With the middleware applied, both RawBody() and Bind() work in any order without issues.