Skip to content

HTTP Response

The Response class represents the outgoing HTTP response for a given request. It wraps H3’s response handling and provides expressive helpers for sending JSON, HTML, plain text, or XML responses — as well as managing headers, status codes, redirects, and caching.

In most cases, you won’t need to instantiate it manually. It’s automatically available through dependency injection or via the HttpContext in callable routes.

Overview

  • Set HTTP status codes and headers
  • Send JSON or plain text responses
  • Return raw HTML and XML content
  • Perform HTTP redirects
  • Access the underlying H3 event

Accessing the Response Instance

Using HttpContext in callable routes

When working directly in callable routes, you can destructure the response (and other properties) from the HttpContext:

ts
import { HttpContext } from '@h3ravel/http';

Route.get(
  '/hello',
  ({ response }: HttpContext) => {
    return response.json({ message: 'Hello World' });
  },
  'hello.route'
);

💡 HttpContext includes { request, response, app, params, route } and other per-request properties.

Injecting via the Service Container

In controllers or services bound to the H3ravel container, you can inject the Response instance directly:

ts
export class UserController extends Controller {
  @Injectable()
  async store(request: Request, response: Response) {
    return response
      .setStatusCode(202)
      .json({ message: `User ${request.input('name')} created` });
  }
}

response() helper

For convinience, H3ravel also provides a response() helper

ts
response()
  .setStatusCode(202)
  .json({ message: `User ${request.input('name')} created` });

When injected, the Response is automatically scoped to the active H3Event and the current Application instance.

Basic Usage

Send JSON

ts
response.json({ success: true });

Send HTML

ts
response.html('<h1>Hello World</h1>');

Send Plain Text

ts
response.text('Plain text message');

Send XML

ts
response.xml('<note>Hello XML</note>');

All send methods can optionally return the raw HTTP response by passing a second argument true:

ts
const raw = response.json({ data: 'ok' }, true);

Redirects

Redirect the client to another URL:

ts
response.redirect('/dashboard');

Or specify a status code and custom status text:

ts
response.redirect('/dashboard', 302, 'Found');

Managing Status Codes

Set or retrieve the current HTTP status code:

ts
response.setStatusCode(201);
console.log(response.getStatusCode()); // 201

Headers and Caching

You can easily attach headers and control cache directives:

ts
response
  .setHeader('X-Powered-By', 'H3ravel')
  .setCache({ public: true, max_age: 3600 });

To set multiple headers at once:

ts
response.withHeaders({
  'X-Custom': 'Example',
  'Cache-Control': 'no-store',
});

Exception Handling

Attach an exception to a response or immediately throw it as an HTTP response exception:

ts
response.withException(new Error('Invalid payload')).throwResponse();

Response State Helpers

The Response class inherits multiple predicates from HttpResponse for quick checks:

MethodDescription
isSuccessful()Returns true if status is 2xx
isClientError()Returns true if status is 4xx
isServerError()Returns true if status is 5xx
isRedirect()Returns true if status is a redirect
isEmpty()Returns true if response has no content
isOk()Returns true if status is 200
isForbidden()Returns true if status is 403
isNotFound()Returns true if status is 404

Debugging

Dump the response object for inspection:

ts
response.dump();

This outputs headers, content, and status information.

Reference Table — Inherited from HttpResponse

MethodDescription
setStatusCode(code, text?)Set HTTP status code and optional reason phrase
getStatusCode()Retrieve current HTTP status code
setCharset(charset)Set the response charset
getCharset()Get current charset
setHeader(name, value)Set a specific header
withHeaders(headers)Merge multiple headers into the response
setProtocolVersion(version)Set the HTTP protocol version (1.0 or 1.1)
getProtocolVersion()Get the protocol version
setPublic()Mark response as cacheable by shared caches
setPrivate()Mark response as private (uncacheable by shared caches)
setCache(options)Configure cache headers (max_age, public, immutable, etc.)
getMaxAge()Get cache max-age (in seconds)
setMaxAge(value)Set cache max-age (in seconds)
setClientTtl(seconds)Set time-to-live for client caches
setSharedMaxAge(value)Set shared (proxy) cache max-age
setTtl(seconds)Set total time-to-live for all caches
getTtl()Get remaining TTL in seconds
getDate()Get the Date header as a DateTime instance
getAge()Get the response age (in seconds)
getExpires()Get the Expires header as a DateTime
getLastModified()Retrieve the Last-Modified header as a DateTime
setLastModified(date?)Set the Last-Modified header
getEtag()Retrieve the current ETag value
setEtag(etag?, weak?)Set or remove the ETag header
isCacheable()Determine if response can be cached
isFresh()Check if response is still fresh
isValidateable()Check if response can be validated via conditional requests
expire()Mark response as stale
setStaleIfError(value)Define stale-if-error directive
setStaleWhileRevalidate(value)Define stale-while-revalidate directive
setNotModified()Transform into a 304 Not Modified response
isInvalid()True if response code is invalid
isInformational()True if status is 1xx
isRedirection()True if status is 3xx
sendHeaders(statusCode?)Send headers before finalizing response
prepare(request)Prepare response before sending (RFC 2616 compliance)
ensureIEOverSSLCompatibility(request)IE-specific compatibility for SSL downloads
withException(e)Attach an exception to this response
throwResponse()Throw the current response as an exception

Quick Examples

Here are a few real-world examples of working with the Response class in H3ravel applications.

1. Returning JSON Data

ts
export class PostController extends Controller {
  @Injectable()
  async index(response: Response) {
    const posts = await Post.all();

    return response.setStatusCode(200).json({ data: posts });
  }
}

💡 All response methods are chainable, so you can set headers, status, and body inline.

2. Returning Paginated Results

ts
import { Post } from 'App/Models/Post';

export class PostController extends Controller {
  @Injectable()
  async paginated(request: Request, response: Response) {
    const page = Number(request.input('page', 1));
    const perPage = 10;

    const result = await Post.query().paginate(page, perPage);

    return response.json({
      data: result.data,
      meta: {
        currentPage: result.currentPage(),
        totalPages: result.total(),
      },
    });
  }
}

3. Sending HTML or Text Responses

ts
Route.get('/welcome', ({ response }: HttpContext) => {
  return response.html('<h1>Welcome to H3ravel!</h1>');
});
ts
Route.get('/ping', ({ response }: HttpContext) => {
  return response.text('pong');
});

4. Redirecting Users

ts
Route.get('/home', ({ response }: HttpContext) => {
  return response.redirect('/dashboard');
});

Or with a custom status:

ts
response.redirect('/login', 301, 'Moved Permanently');

5. Sending Custom Headers

ts
response
  .setHeader('X-Powered-By', 'H3ravel')
  .setHeader('Cache-Control', 'no-store')
  .json({ status: 'ok' });

You can also merge multiple headers at once:

ts
response.withHeaders({
  'X-API-Version': '1.0',
  'X-RateLimit-Limit': '60',
});

6. Returning an Error Response

ts
export class AuthController extends Controller {
  @Injectable()
  async login(request: Request, response: Response) {
    const user = await User.verify(
      request.input('email'),
      request.input('password')
    );

    if (!user) {
      return response.setStatusCode(401).json({ error: 'Invalid credentials' });
    }

    return response.json({ token: user.generateToken() });
  }
}

7. Attaching Exceptions

If a response represents an error, you can attach the exception for downstream handling:

ts
response
  .withException(new Error('User not found'))
  .setStatusCode(404)
  .json({ message: 'User not found' });

Or immediately throw it as a HttpResponseException:

ts
response
  .withException(new Error('Forbidden'))
  .setStatusCode(403)
  .throwResponse();

8. Caching Responses

ts
response.setCache({ public: true, max_age: 3600 }).json({ cached: true });

Or mark as private:

ts
response
  .setPrivate()
  .setCache({ max_age: 0 })
  .json({ message: 'Private data' });

9. Combining With Requests

ts
Route.post('/users', async ({ request, response }: HttpContext) => {
  const name = request.input('name');

  // Create the user...
  return response
    .setStatusCode(201)
    .json({ message: `User ${name} created successfully` });
});

10. Streaming or Sending Files

When working at the edge or using H3 native response APIs, you can stream directly through the event:

ts
export class StreamController extends Controller {
  @Injectable()
  async stream(request: Request, response: Response) {
    let interval: NodeJS.Timeout;
    const stream = new ReadableStream({
      start(controller) {
        controller.enqueue('<ul>');

        interval = setInterval(() => {
          controller.enqueue('<li>' + Math.random() + '</li>');
        }, 100);

        setTimeout(() => {
          clearInterval(interval);
          controller.close();
        }, 1000);
      },
      cancel() {
        clearInterval(interval);
      },
    });

    return response
      .setStatusCode(200)
      .setHeader('Content-Type', 'text/html')
      .setHeader('Cache-Control', 'no-cache')
      .setHeader('Transfer-Encoding', 'chunked')
      .setContent(stream);
  }
}

In H3ravel, Response integrates seamlessly with the underlying H3Event, so you can use native H3 utilities like send, sendStream, and setHeader when necessary.

11. Dumping Response for Debugging

ts
response.setStatusCode(200).json({ debug: true }).dump();

This prints all internal response data to the console (useful for development).

Summary

The Response class provides a fluent, chainable API for building web responses that feel natural and readable. Whether you’re returning JSON APIs, HTML views, redirects, or custom headers — Response gives you the flexibility to handle every scenario in a unified way, letting you quickly return content, configure headers, manage cache policies, or perform redirects.

Released under the MIT License.