=== Responses

This chapter covers special response types, including raw responses, streaming, file downloads, and non-blocking responses.

==== Raw

Raw responses are **not** processed by a <<core-context-response-body-message-encoder, message encoder>>. The following types are considered `raw`:

* `String` / `CharSequence`
* `byte[]`
* `java.nio.ByteBuffer` / `io.netty.buffer.ByteBuf`
* `java.io.File` / `java.io.InputStream` / `java.nio.file.Path` / `java.nio.channels.FileChannel`

.Generate a JSON String from a handler
[source,java,role="primary"]
----
{
  get("/json", ctx -> {
    ctx.setContentType(MediaType.json);
    return "{\"message\": \"Hello Raw Response\"}";
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  get("/json") {
    ctx.contentType = MediaType.json
    """
      "message": "Hello Raw Response"
    """
  }
}
----

Even if a JSON encoder is installed, a raw response is always sent directly to the client bypassing the encoder.

==== Projections

Projections allow API consumers to request a partial representation of a resource. This feature is a more flexible, dynamic, and powerful alternative to the standard Jackson `@JsonView` annotation.

While inspired by the "selection set" philosophy of **GraphQL**, it is important to note that this is **not** a GraphQL implementation. Instead, it provides a "Light GraphQL" experience for RESTful endpoints, allowing you to define exactly which fields should be serialized in the JSON response without the overhead of a full GraphQL engine.

===== Basic Usage

To enable a projection, you wrap your response object using the `Projected.wrap` utility. The projection syntax is parsed and applied to the underlying object or collection.

[source,java]
----
get("/users/{id}", ctx -> {
  User user = repository.findById(ctx.path("id").value());
  
  return Projected.wrap(user)
      .include("(id, name, email)");
});
----

===== Comparison with @JsonView

If you have used Jackson's `@JsonView`, you will find Projections far more capable:

* **Dynamic**: Unlike `@JsonView`, which requires static class markers defined at compile-time, Projections are defined at runtime.
* **Ad-hoc**: You can create any combination of fields on the fly without adding new Java interfaces or classes.
* **Deep Nesting**: Projections easily handle deeply nested object graphs, whereas `@JsonView` can become difficult to manage with complex relationships.

===== Projection DSL

The `include` method accepts a string using a simple, nested syntax:

* **Field Selection**: `(id, name)` returns only those two fields.
* **Nested Selection**: `(id, address(city, country))` selects the `id` and specific fields from the nested `address` object.
* **Wildcards**: `(id, address(*))` selects the `id` and all available fields within the `address` object.
* **Deep Nesting**: `(id, orders(id, items(name, price)))` allows for recursion into the object graph.

===== Validation

By default, the projection engine **does not validate** that requested fields exist on the target class (`failOnMissingProperty` is `false`). This allows for maximum flexibility, especially when working with polymorphic types or dynamic data where certain fields may only exist on specific subclasses.

If you prefer strict enforcement to prevent API consumers from requesting non-existent fields, you can enable validation:

[source,java]
----
return Projected.wrap(data)
    .failOnMissingProperty(true)
    .include("(id, name, strictFieldOnly)");
----

===== More Information

Support for Projections extends beyond core scripting to include high-level annotations and documentation generation.

* **MVC Support**: Projections can be applied to controller methods using the `@Project` annotation. See the <<web-mvc-api-responses-projections, MVC documentation>> for details.
* **OpenAPI Support**: Jooby automatically generates pruned schemas for your Swagger documentation. See the link:modules/openapi[OpenAPI documentation] for details.

[NOTE]
====
**Implementation Note:**
The `Projection` core API defines the structure and the DSL. The actual runtime filtering is performed by your chosen JSON module:

1. link:modules/avaje-jsonb[Avaje Jsonb]
2. link:modules/jackson2[Jackson 2]/link:modules/jackson3[Jackson 3]
====

==== Streaming / Chunked

The Streaming/Chunked API is available via:

* javadoc:Context[responseStream]: A blocking API that provides an `OutputStream`.
* javadoc:Context[responseWriter]: A blocking API that provides a `PrintWriter`.
* javadoc:Context[responseSender]: A non-blocking API that provides a javadoc:Sender[].

You can only call **one** of these methods per request. When you call one of them, Jooby automatically adds the `Transfer-Encoding: chunked` header if the `Content-Length` is missing.

All three APIs have a `close` method, which you must call when finished.

.Writer example
[source,java,role="primary"]
----
{
  get("/chunk", ctx -> {
    try(Writer writer = ctx.responseWriter()) { // <1>
      writer.write("chunk1");                   // <2>
      // ...
      writer.write("chunkN");
    }

    return ctx;                                 // <3>
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  get("/chunk") {
    ctx.responseWriter().use {                  // <1>
      writer.write("chunk1")                    // <2>
      // ...
      writer.write("chunkN")
    }
    ctx                                         // <3>
  }
}
----

<1> Get the `Writer` inside a try-with-resources (or `use` in Kotlin) block so it closes automatically.
<2> Write chunks of data.
<3> Return the `Context`.

There is an overloaded version (primarily for Java) that lets you skip the try-with-resources block and automatically closes the writer/stream for you:

.Auto-closing Writer example
[source,java,role="primary"]
----
{
  get("/chunk", ctx -> {
    return ctx.responseWriter(writer -> { // <1>
      writer.write("chunk1");             // <2>
      // ...
      writer.write("chunkN");
    });
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  get("/chunk") {
    ctx.responseWriter {                  // <1>
      writer.write("chunk1")              // <2>
      // ...
      writer.write("chunkN")
    }
  }
}
----

==== File Download

Use javadoc:FileDownload[] to generate file downloads (responses with a `Content-Disposition` header). You can use the convenience subclasses javadoc:AttachedFile[] or javadoc:InlineFile[] to set the header value to `attachment` or `inline`, respectively.

.File download example
[source,java,role="primary"]
----
{
  get("/download-file", ctx -> {
    Path source = Paths.get("logo.png");
    return new AttachedFile(source);             // <1>
  });
  
  get("/view-stream", ctx -> {
    InputStream source = ...;
    return new InlineFile("myfile.txt", source); // <2>
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  get("/download-file") {
    val source = Paths.get("logo.png")
    AttachedFile(source)                // <1>
  }

  get("/view-stream") {
    val source = ...
    InlineFile("myfile.txt", source)    // <2>
  }
}
----

<1> Send a download from a `Path` as an attachment.
<2> Send a download from an `InputStream` inline.

Alternatively, you can use the static builder methods on `FileDownload` and specify the download type later.

.File download with builder method
[source,java,role="primary"]
----
FileDownload.Builder produceDownload(Context ctx) {
  return FileDownload.build(...);
}

{
  get("/view", ctx -> produceDownload(ctx).inline());
  
  get("/download", ctx -> produceDownload(ctx).attachment());
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
fun produceDownload(ctx: Context) = FileDownload.build(...)

{
  get("/view") {
    produceDownload(it).inline()
  }

  get("/download") {
    produceDownload(it).attachment()
  }
}
----

==== NonBlocking

From the user's perspective, there is nothing special about writing non-blocking responses—you write your route handler the same way you usually do. However, it's important to understand how they execute in the pipeline based on your application's mode.

.In event loop mode
[source,java,role="primary"]
----
{
  mode(EVENT_LOOP);                  // <1>
  use(ReactiveSupport.concurrent()); // <2>

  get("/non-blocking", ctx -> {
    return CompletableFuture         // <3>
        .supplyAsync(() -> {
          // ...                     // <4>
        });
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  mode(EVENT_LOOP)                   // <1>
  use(ReactiveSupport.concurrent())  // <2>

  get("/non-blocking") {
    CompletableFuture                // <3>
        .supplyAsync {
          // ...                     // <4>
        }
  }
}
----

<1> The application runs in the **event loop**.
<2> Indicates we want to go non-blocking and handle CompletableFuture responses.
<3> The value is provided from the event loop. **No blocking code is permitted.**
<4> The value is computed asynchronously.

Running your application in **worker** mode works identically, except you are allowed to make blocking calls:

.In worker mode
[source,java,role="primary"]
----
{
  mode(WORKER);                      // <1>
  use(ReactiveSupport.concurrent()); // <2>

  get("/blocking", ctx -> {
    return CompletableFuture         // <3>
        .supplyAsync(() -> {
          // ...                     // <4>
        });
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  mode(WORKER)                       // <1>
  use(ReactiveSupport.concurrent())  // <2>

  get("/blocking") {
    CompletableFuture                // <3>
        .supplyAsync {
          // ...                     // <4>
        }
  }
}
----

<1> The application runs in **worker mode**.
<2> Indicates we want to go non-blocking.
<3> The value is provided from the worker thread. **Blocking code is permitted.**
<4> The value is computed asynchronously.

The **default** mode mimics the event loop mode when a route produces a non-blocking type:

.In default mode
[source,java,role="primary"]
----
{
  mode(DEFAULT);                     // <1>
  use(ReactiveSupport.concurrent()); // <2>

  get("/non-blocking", ctx -> {
    return CompletableFuture         // <3>
        .supplyAsync(() -> {
          // ...                     // <4>
        });
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  mode(DEFAULT)                     // <1>

  get("/non-blocking") {
    // ...                          // <2>
    CompletableFuture               // <3>
        .supplyAsync {
          // ...                    // <4>
        }
  }
}
----

[NOTE]
====
For all reactive frameworks below, explicit handler setup (e.g., `use(Reactivex.rx())`) is **only** required for Script/Lambda routes. For MVC routes, Jooby automatically configures the handler based on the route's return type.
====

===== CompletableFuture

CompletableFuture is a non-blocking type that produces a single result:

.Java
[source,java, role="primary"]
----
{
  use(ReactiveSupport.concurrent());

  get("/non-blocking", ctx -> {
    return CompletableFuture
        .supplyAsync(() -> "Completable Future!")
        .thenApply(it -> "Hello " + it);
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  use(ReactiveSupport.concurrent())

  get("/non-blocking") {
    CompletableFuture
        .supplyAsync { "Completable Future!" }
        .thenApply { "Hello $it" }
  }
}
----

===== Mutiny

1) Add the https://smallrye.io/smallrye-mutiny[SmallRye Mutiny] dependency:
[dependency, artifactId="jooby-mutiny"]

2) Write the code:

.Uni (Single Value)
[source,java, role="primary"]
----
import io.jooby.mutiny;
import io.smallrye.mutiny.Uni;

{
  use(Mutiny.mutiny());

  get("/non-blocking", ctx -> {
    return Uni.createFrom()
       .completionStage(supplyAsync(() -> "Uni"))
       .map(it -> "Hello " + it);
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.mutiny
import io.smallrye.mutiny.Uni

{
  use(Mutiny.mutiny())

  get("/non-blocking") {
    Uni.createFrom()
      .completionStage { supplyAsync { "Uni" } }
      .map { "Hello $it" }
  }
}
----

.Multi (Chunked Stream)
[source,java, role="primary"]
----
import io.jooby.mutiny;
import io.smallrye.mutiny.Multi;

{
  use(Mutiny.mutiny());

  get("/non-blocking", ctx -> {
    return Multi.createFrom().range(1, 11)
        .map(it -> it + ", ");
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.mutiny
import io.smallrye.mutiny.Multi

{
  use(Mutiny.mutiny())

  get("/non-blocking") {
    Multi.createFrom().range(1, 11)
        .map { "$it, " }
  }
}
----

For `Multi`, Jooby automatically builds a chunked response. Each item in the stream is sent to the client as a new HTTP chunk.

===== RxJava

1) Add the https://github.com/ReactiveX/RxJava[RxJava] dependency:
[dependency, artifactId="jooby-rxjava3"]

2) Write the code:

.Single
[source,java, role="primary"]
----
import io.jooby.rxjava3.Reactivex;

{
  use(Reactivex.rx());

  get("/non-blocking", ctx -> {
    return Single
        .fromCallable(() -> "Single")
        .map(it -> "Hello " + it);
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.rxjava3.Reactivex

{
  use(Reactivex.rx())

  get("/non-blocking") {
    Single
        .fromCallable { "Single" }
        .map { "Hello $it" }
  }
}
----

.Flowable (Chunked Stream)
[source,java, role="primary"]
----
import io.jooby.rxjava3.Reactivex;

{
  use(Reactivex.rx());

  get("/non-blocking", ctx -> {
    return Flowable.range(1, 10)
        .map(it -> it + ", ");
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.rxjava3.Reactivex

{
  use(Reactivex.rx());

  get("/non-blocking") {
    Flowable.range(1, 10)
        .map { "$it, " }
  }
}
----

For `Flowable`, Jooby builds a chunked response, sending each item as a separate chunk.

===== Reactor

1) Add the https://projectreactor.io/[Reactor] dependency:
[dependency, artifactId="jooby-reactor"]

2) Write the code:

.Mono (Single Value)
[source,java, role="primary"]
----
import io.jooby.Reactor;

{
  use(Reactor.reactor());

  get("/non-blocking", ctx -> {
    return Mono
        .fromCallable(() -> "Mono")
        .map(it -> "Hello " + it);
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.Reactor

{
  use(Reactor.reactor())

  get("/non-blocking") {
    Mono
        .fromCallable { "Mono" }
        .map { "Hello $it" }
  }
}
----

.Flux (Chunked Stream)
[source,java, role="primary"]
----
import io.jooby.Reactor;

{
  use(Reactor.reactor());

  get("/non-blocking", ctx -> {
    return Flux.range(1, 10)
        .map(it -> it + ", ");
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.Reactor

{
  use(Reactor.reactor())

  get("/non-blocking") {
    Flux.range(1, 10)
        .map { "$it, " }
  }
}
----

For `Flux`, Jooby builds a chunked response, sending each item as a separate chunk.

===== Kotlin Coroutines

*(Note: Coroutines are exclusive to Kotlin, so there is no Java equivalent for this section).*

.Coroutine handler:
[source, kotlin]
----
{
  coroutine {
    get("/") {
      delay(100)           // <1>
      "Hello Coroutines!"  // <2>
    }
  }
}
----

<1> Call a suspending function.
<2> Send the response to the client.

.Using an extension and suspending function:
[source, kotlin]
----
{
  coroutine {
    get("/") {
      ctx.doSomething()         // <1>
    }
  }
}

suspend fun Context.doSomething(): String {
  delay(100)                  // <2>
  return "Hello Coroutines!"  // <3>
}
----

<1> Call an extension suspending function.
<2> Safely perform a suspending or blocking call.
<3> Send the response to the client.

Coroutines work like any other non-blocking type. If you start Jooby using the **event loop** or **default mode**, Jooby creates a coroutine context to execute it.

Jooby uses the **worker executor** to create a coroutine context. This executor is provided by the web server implementation, unless you provide a custom one:

.Coroutines with a custom executor:
[source, kotlin]
----
{
  worker(Executors.newCachedThreadPool())

  coroutine {
    get("/") {
      val n = 5 * 5        // <1>
      delay(100)           // <2>
      "Hello Coroutines!"  // <3>
    }
  }
}
----

<1> Statement runs in the worker executor (cached thread pool).
<2> Calls a suspending function.
<3> Produces a response.

By default, Coroutines always run in the worker executor. However, Jooby provides an experimental API where coroutines run in the **caller thread** (the event loop) until a suspending function is found. You can enable this by setting the `coroutineStart` option:

.UNDISPATCHED Start
[source, kotlin]
----
{
  coroutine(CoroutineStart.UNDISPATCHED) {
    get("/") {
      val n = 5 * 5        // <1>
      delay(100)           // <2>
      "Hello Coroutines!"  // <3>
    }
  }
}
----

<1> Statement runs in the event loop (caller thread).
<2> Calls a suspending function and dispatches to the worker executor.
<3> Produces a response from the worker executor.

You can also extend the `CoroutineContext` in which the routes run:

.Customizing the Context
[source, kotlin]
----
{
  coroutine {
    launchContext { MDCContext() } // <1>
  
    get("/") {
      // ...
    }
  }
}
----

<1> The `launchContext` lambda runs before launching each coroutine. It allows you to customize the `CoroutineContext` for the request (e.g., to store/restore MDC, transactions, or other request-scoped data).

==== Send Methods

Jooby provides a family of `send()` methods that produce a response via side-effects.

.send text
[source,java,role="primary"]
----
{
  get("/", ctx -> {
    return ctx.send("Hello World!");
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  get("/") {
    ctx.send("Hello World!")
  }
}
----

Although these methods operate via side-effects, the route handler must still return a value. All `send` methods return the current `Context`. Returning the context signals to Jooby that the response was already handled and the standard route output should be ignored.

The family of send methods includes:

* javadoc:Context[send, byte[\]]
* javadoc:Context[send, byte[\]...]
* javadoc:Context[send, io.jooby.buffer.DataBuffer]
* javadoc:Context[send, io.jooby.FileDownload]
* javadoc:Context[send, io.jooby.StatusCode]
* javadoc:Context[send, java.io.InputStream]
* javadoc:Context[send, java.lang.String]
* javadoc:Context[send, java.lang.String, java.nio.charset.Charset]
* javadoc:Context[send, java.nio.ByteBuffer]
* javadoc:Context[send, java.nio.ByteBuffer[\]]
* javadoc:Context[send, java.nio.channels.FileChannel]
* javadoc:Context[send, java.nio.channels.ReadableByteChannel]
* javadoc:Context[send, java.nio.file.Path]
