The Basics
Request
Introduction
Every HTTP handler receives an Express Request object. JCC Express MVC extends it as AppRequest and passes the same instance as req in route closures and in { req, res, next } / HttpContext patterns. After the framework’s request middleware runs, Laravel-style helpers are bound onto req, so you call req.input(), req.header(), and so on.
You can also use the global request() helper, which resolves the current AppRequest from the container (only valid during an HTTP request, after the per-request binding middleware runs).
Types: import Request from jcc-express-mvc as the AppRequest alias when you need to annotate parameters (for example FormRequest subclasses). For res, response(), Inertia, flash, and redirectBack, see Response.md in this folder.
When helpers are available
Helpers are attached after the framework request middleware runs (see Request lifecycle). Anything that runs before that middleware only sees a plain Express request.
Input from query, body, and merged data
These methods merge query and body for reads (input); file fields are handled separately via file / hasFile / store.
req.input()— All merged input, orreq.input(key, default?)for one key.req.all()— Merged query and body as a plain object ({ ...query, ...body }).req.has(key)— Whether the key exists in merged input.req.filled(key)— Present and non-empty string.req.only(...keys)— Subset of keys.req.except(...keys)— Copy of input without those keys (mutates the merged object for the copy path in the implementation—avoid relying on retaining the full original reference).req.merge(data)— Shallow assign ontoreq.body.req.replace(data)— Replacereq.bodyentirely.
Route parameters remain on req.params (see Routing). input() does not include params; use req.params.id or route model binding in controllers.
Headers, content negotiation, and API-ish detection
req.header(name)— Case-insensitive header lookup.req.ajax()—X-Requested-With: XMLHttpRequest.req.isJson()— JSONContent-Type.req.wantsJson(),req.acceptsJson()—Accepthints.req.expectsJson()— Combines API path prefix (/api), AJAX, and JSON signals.req.isInertia()— Presence ofX-Inertia.req.isApi()—truewhen the path starts with/api.
Use these to branch between JSON errors and HTML redirects, or to tailor Inertia responses.
Auth, session, and flash
req.user— Set by auth middleware when a user is authenticated (shape depends on your auth setup).req.session— Express session (requires session middleware).req.flash(...)— Flash API fromconnect-flash(same session stack).req.jccSession— Framework session bridge when available.- Global
session()returnsreq.jccSessionwhen you use the helper style.
Details: put / get / save / regenerate, flash alignment with res.with, and configuration notes are in Session.md.
Validation
req.validate(rules, customMessages?)— Validates against the current request (see Validation).req.validated()— Retrieves validated data after validation has run (when your pipeline stores it).
The global validate(...) helper resolves the current request and calls validate on it.
Files and cookies
Upload handling relies on express-fileupload (registered in the framework middleware stack). Uploaded fields are available on req.files.
req.hasFile(key)— Whether an uploaded file field is present.req.cookie(key)— Parsed cookies (requirescookie-parser).
Storing uploads to disk (file + store)
Use a fluent chain: call req.file(fieldName) to select the field, then req.store(directory) on the same req to persist it.
| Method | Returns | Description |
|---|---|---|
req.file(fileName) | AppRequest (chainable) | Remembers the field name for the next store() call. |
req.store(directory) | string | string[] | Saves the file(s) and returns the generated filename(s). Clears the remembered field after storing. |
Behavior matches the framework’s saveImage helper for local disk storage.
- Files are written under
public/<directory>/(created if missing). - Each filename is slugged from the original name plus a random suffix; the original extension (or mimetype-derived extension) is preserved.
- When the field contains multiple files (array), every file is saved and an array of filenames is returned.
store() is synchronous and returns immediately with the generated name(s); the underlying mv call runs asynchronously—handle errors in your route or wrap in try/catch if you need strict failure handling.
Uploading to Cloudinary (storeToCloudinary)
Upload a field directly to Cloudinary without saving to disk first. First run bun artisanNode publish Cloudinary to create app/Config/cloudinary.ts and register CloudinaryServiceProvider. Then set Cloudinary credentials in .env (CLOUDINARY_NAME, CLOUDINARY_API_KEY, CLOUDINARY_SECRET). See Cloudinary for setup details.
| Parameter | Type | Default | Description |
|---|---|---|---|
fileName | string | — | The req.files field name to upload. |
option | Record<string, any> | {} | Options forwarded to cloudinary.uploader.upload (e.g. folder, resource_type). |
returnUrl | boolean | true | When true, returns res.url (or an array of URLs). When false, returns the full upload response object(s). |
When the field contains multiple files, storeToCloudinary uploads each one and returns an array (URLs or full responses, depending on returnUrl).
FormRequest equivalents
FormRequest subclasses expose the same upload API:
Other helpers
req.bearerToken()— ParsesAuthorization: Bearer ….req.userAgent()—User-Agentheader.req.isMethod("post")— Case-insensitive HTTP method check.req.fullUrl()— Protocol, host, andoriginalUrl.req.csrfToken()— Available whencsrf()middleware ran; same value asres.locals._token(see CSRF-protection).
Express-native fields such as req.path, req.query, req.body, req.params, req.ip, and req.get("host") remain available.
FormRequest (action injection)
For validated or authorized form flows, extend FormRequest (jcc-express-mvc/Core/FormRequest) and type-hint the subclass on a controller method decorated with @Method(). The container constructs it with the current AppRequest, runs await rules() (your async or sync rules hook), then calls the action.
Typical pattern:
- Implement
protected async rules()(or sync) and callthis.validate({ field: ["required"], … })with rule arrays. - Optionally override
protected authorize()— the base implementation returnsfalse. If your override returnstrue, the constructor throwsAuthorizationException(deny). Returnfalseto allow the request to continue.
Expose domain actions such as save() on your subclass (UserRequest, etc.) and call them from the controller after injection.
Summary
AppRequest= ExpressRequest+ framework helpers bound per request.- Access via
req,request(), orHttpContext(parameter defaults only for context objects—see Routing). - Prefer
req.input/only/validatefor Laravel-like ergonomics; keepreq.paramsfor route segments and binding. - File uploads:
req.file(name).store(directory)for local disk (public/<directory>/), orawait req.storeToCloudinary(name, options?, returnUrl?)for Cloudinary. - Sending the response:
Response.md(AppResponse,res.inertia,res.with,redirectBack, globalsview/redirect/back/inertia). - Session and flash:
Session.md.