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:
import { HttpContext } from '@h3ravel/http';
Route.get(
'/hello',
({ response }: HttpContext) => {
return response.json({ message: 'Hello World' });
},
'hello.route'
);💡
HttpContextincludes{ 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:
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
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
response.json({ success: true });Send HTML
response.html('<h1>Hello World</h1>');Send Plain Text
response.text('Plain text message');Send XML
response.xml('<note>Hello XML</note>');All send methods can optionally return the raw HTTP response by passing a second argument true:
const raw = response.json({ data: 'ok' }, true);Redirects
Redirect the client to another URL:
response.redirect('/dashboard');Or specify a status code and custom status text:
response.redirect('/dashboard', 302, 'Found');Managing Status Codes
Set or retrieve the current HTTP status code:
response.setStatusCode(201);
console.log(response.getStatusCode()); // 201Headers and Caching
You can easily attach headers and control cache directives:
response
.setHeader('X-Powered-By', 'H3ravel')
.setCache({ public: true, max_age: 3600 });To set multiple headers at once:
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:
response.withException(new Error('Invalid payload')).throwResponse();Response State Helpers
The Response class inherits multiple predicates from HttpResponse for quick checks:
| Method | Description |
|---|---|
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:
response.dump();This outputs headers, content, and status information.
Reference Table — Inherited from HttpResponse
| Method | Description |
|---|---|
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
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
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
Route.get('/welcome', ({ response }: HttpContext) => {
return response.html('<h1>Welcome to H3ravel!</h1>');
});Route.get('/ping', ({ response }: HttpContext) => {
return response.text('pong');
});4. Redirecting Users
Route.get('/home', ({ response }: HttpContext) => {
return response.redirect('/dashboard');
});Or with a custom status:
response.redirect('/login', 301, 'Moved Permanently');5. Sending Custom Headers
response
.setHeader('X-Powered-By', 'H3ravel')
.setHeader('Cache-Control', 'no-store')
.json({ status: 'ok' });You can also merge multiple headers at once:
response.withHeaders({
'X-API-Version': '1.0',
'X-RateLimit-Limit': '60',
});6. Returning an Error Response
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:
response
.withException(new Error('User not found'))
.setStatusCode(404)
.json({ message: 'User not found' });Or immediately throw it as a HttpResponseException:
response
.withException(new Error('Forbidden'))
.setStatusCode(403)
.throwResponse();8. Caching Responses
response.setCache({ public: true, max_age: 3600 }).json({ cached: true });Or mark as private:
response
.setPrivate()
.setCache({ max_age: 0 })
.json({ message: 'Private data' });9. Combining With Requests
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:
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,
Responseintegrates seamlessly with the underlyingH3Event, so you can use native H3 utilities likesend,sendStream, andsetHeaderwhen necessary.
11. Dumping Response for Debugging
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.