Honovel Honovel Deno

Controllers in Honovel

Controllers in Honovel handle your application’s request logic. They act as the bridge between routes and your models or views, keeping your code organized and maintainable. Inspired by Laravel, controllers encourage clean separation of concerns.

Creating a Controller

You can create a controller manually or use a scaffold command:

deno task smelt make:controller UserController

This generates a file in app/Http/Controllers/UserController.ts with a basic class structure.

Basic Controller Structure

import Controller from "App/Http/Controllers/Controller.ts";

export default class UserController extends Controller {
    public async index({ request }) {
        // your logic here
    }
}

Binding Route Parameters

Honovel does not currently support automatic model binding. To bind route parameters to models, you need to define them in the SubstituteBindings middleware:

// vendor/honovel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.ts

private readonly routerBindings: Record<string, typeof Model<ModelWithAttributes>> = {
    user: User,   // Maps {user} route parameter to the User model
    post: Post    // Maps {post} route parameter to the Post model
};

Then, in your controller method, you can type-hint the model when retrieving the parameter:

public async show({ request }, {user}:{user: User}) {
    // 'user' is fetched according to the routerBindings mapping
}

Returning Responses

In Honovel, you use the global response() function, which returns a HonoResponseV2 instance. This allows you to return JSON, HTML, files, downloads, or streams.

  • JSON: return response().json({ success: true })
  • HTML: return response().html("<h1>Hello</h1>")
  • File: return response().file("path/to/file")
  • Download: return response().download("path/to/file", "filename.ext")
  • Stream: return response().stream("path/to/file")
  • Status and Headers: Chainable methods status(code), header(key, value), and withHeaders({...}) can be used to customize responses.

Example: Full UserController

import Controller from "App/Http/Controllers/Controller.ts";
import User from "App/Models/User.ts";

export default class UserController extends Controller {
    public async index({ request }) {
        const users = await User.all();
        return response().json(users);
    }

    public async show({ request }, {id}) {
        const user = await User.find(id);
        if (!user) {
            return response().status(404).json({ error: "User not found" });
        }
        return response().json(user);
    }

    public async store({ request }) {
        const data = await request.body().value;
        const user = await User.create(data);
        return response().status(201).json(user);
    }

    public async update({ request }, {id}) {
        const user = await User.find(id);
        const data = await request.body().value;
        await user.update(data);
        return response().json(user);
    }

    public async destroy({ request }, {id}) {
        const user = await User.find(id);
        await user.delete();
        return response().json({ success: true });
    }

    public async downloadReport() {
        return response().download("storage/reports/report.pdf", "report.pdf");
    }
}

Next Steps

Pair your controllers with routes to build fully functional pages or APIs. Advanced topics like middleware chaining and model binding are covered in their dedicated documentation sections.