Controllers & Dependency Injection
Controllers group related HTTP request handling logic into single ES6 classes. StruxJS manages Controllers via its built-in IoC Container, providing automatic dependency injection into constructors, action methods, and class properties.
Creating Controllers
Use the StruxJS CLI to generate Controllers.
Basic Controller
Generate a basic Controller scaffold in app/Controllers/:
npx strux make:controller UserControllerGenerated file (app/Controllers/UserController.ts):
import { Request, Response } from "struxjs";
export class UserController {
public async index(request: Request, response: Response) {
return response.json({
message: "Hello from UserController!"
});
}
}Resource Controller (--resource)
Generate a Resource Controller containing standard CRUD action methods:
npx strux make:controller UserController --resourceThis generates index, create, store, show, edit, update, and destroy method stubs.
Binding Controllers to Routes
Bind Controller action methods to HTTP routes using Tuple Array or String Action syntax:
1. Type-Safe Tuple Array (Recommended)
import { Route } from "struxjs";
import { UserController } from "../app/Controllers/UserController.js";
Route.get("/users", [UserController, "index"]).name("users.index");
Route.post("/users", [UserController, "store"]).name("users.store");
Route.get("/users/:id", [UserController, "show"]).name("users.show");2. String Action Syntax
Route.get("/profile", "UserController@profile").name("profile");Custom String Token Bindings
In addition to Class types, services and dependencies can be bound to custom String tokens inside app/Providers/AppServiceProvider.ts:
// app/Providers/AppServiceProvider.ts
import { Container } from "struxjs";
import { PostService } from "../Services/PostService.js";
import { AuthMiddleware } from "../Middleware/AuthMiddleware.js";
export class AppServiceProvider {
public register(container: Container): void {
// Register custom string token bindings
container.singleton("post.service", (c) => c.make(PostService));
container.bind("custom-auth", (c) => c.make(AuthMiddleware));
}
}Dependency Injection
StruxJS supports multiple dependency injection strategies.
1. Constructor Injection
A. Direct Type Hinting
Inject services directly by type-hinting them in the Controller constructor:
import { UserService } from "../Services/UserService.js";
import { PostService } from "../Services/PostService.js";
export class UserController {
constructor(
private userService: UserService,
private postService: PostService
) {}
public async index() {
return await this.userService.getAll();
}
}B. String Token Injection via @Inject()
Inject custom string tokens using the @Inject("token_name") decorator:
import { Inject } from "struxjs";
import { PostService } from "../Services/PostService.js";
export class UserController {
constructor(
@Inject("post.service") private postService: PostService
) {}
}2. Method Action Injection
StruxJS resolves action parameters automatically. You can inject Request, Response, FormRequest validation classes, application Services, or Route Parameters matching by parameter name:
A. Injecting Request, Response & Services
import { Request, Response } from "struxjs";
import { RegisterRequest } from "../Requests/RegisterRequest.js";
import { UserService } from "../Services/UserService.js";
// 1. Inline Validation via request.validate()
export class UserController {
public async store(
request: Request,
response: Response,
userService: UserService
) {
const validatedData = await request.validate({
email: "required|email",
name: "required|min:2"
});
const user = await userService.create(validatedData);
return response.json(user);
}
}
// 2. FormRequest Class Validation via camelCase Parameter Name
export class UserController {
public async store(
registerRequest: RegisterRequest,
response: Response,
userService: UserService
) {
const validatedData = registerRequest.validated(); // Or registerRequest.all()
const user = await userService.create(validatedData);
return response.json(user);
}
}FormRequest Parameter Naming Convention (camelCase Rule)
When injecting custom FormRequest classes (such as RegisterRequest or LoginRequest), name the action parameter in camelCase matching the Class name (e.g., registerRequest: RegisterRequest or loginRequest: LoginRequest).
Generic parameter names like request: Request or req: Request are reserved for resolving the standard HTTP Request object.
B. Direct Route Parameter Injection
StruxJS automatically matches route parameter names (:id, :postId, :commentId) to method parameter names:
// Route: Route.get("/users/:id", [UserController, "show"])
export class UserController {
public async show(id: string) {
return { userId: id };
}
}// Route: Route.get("/posts/:postId/comments/:commentId", [CommentController, "show"])
export class CommentController {
public async show(postId: string, commentId: string) {
return { postId, commentId };
}
}C. Combined Method Injection
Combine Request, Response, Services, and Route Parameters in any order:
// Route: Route.get("/users/:id", [UserController, "show"])
export class UserController {
public async show(
request: Request,
response: Response,
id: string,
userService: UserService
) {
const user = await userService.find(id);
return response.json(user);
}
}3. Manual Resolution (make())
Resolve dependencies manually using the make() helper by passing a Class Constructor directly or a String Token:
import { make } from "struxjs";
import { UserService } from "../Services/UserService.js";
import { PostService } from "../Services/PostService.js";
export class UserController {
public async index() {
// 1. Passing Class Constructor directly
const userService = make(UserService);
// 2. Passing custom String Token
const postService = make<PostService>("post.service");
return await userService.getAll();
}
}Action Return Types
Controller action methods support various return types:
1. String
Returning a string sends a text/plain or text/html response:
public async index() {
return "Hello World";
}2. Array / Object (JSON)
Returning an Object or Array automatically serializes the data to JSON with an application/json header:
public async index() {
return {
success: true,
data: [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
]
};
}3. Server-Rendered View (response.view)
Render HTML templates (.strux) from resources/views/:
public async index(request: Request, response: Response) {
const users = await this.userService.getAll();
return response.view("users.index", { users });
}4. HTTP Redirects (response.redirect)
Direct Redirect
public async store(request: Request, response: Response) {
return response.redirect("/login");
}Named Route Redirect
public async store(request: Request, response: Response) {
return response.redirect().route("users.show", { id: 42 });
}Back Redirect
public async store(request: Request, response: Response) {
return response.redirect().back();
}Redirect with Input & Flash Session
public async store(request: Request, response: Response) {
return response.redirect()
.route("users.create")
.withInput()
.with("error", "Email address already registered.");
}