Odoo gives developers a flexible backend framework that can be customized to suit different needs. One important part of this framework is the HTTP Controller. Controllers help us create custom routes and manage HTTP requests, which can be used to build APIs, webhooks, AJAX calls, or custom web pages.
In Odoo 19, the HTTP controller feature is still simple to use and helps connect the frontend with the backend smoothly and securely.
In this blog, we’ll look at what HTTP controllers are and learn about their main parts — routes, requests, responses, authentication, cookies, JSON APIs, mixins, and some useful best practices.
Controllers
In Odoo, a controller is a Python class that inherits from http.Controller. It is used to group together routes that serve a similar purpose. Usually, controllers are stored inside a module’s controllers/ folder. When the module is installed, Odoo automatically detects and registers them, so they can start handling requests right away.
For example, a simple controller could be written as follows:
from odoo import http
from odoo.http import request
class MyController(http.Controller):
@http.route('/hello', type='http', auth='public')
def say_hello(self, **kwargs):
return "Hello, world!"
Here, opening the /hello URL in a browser will return a plain text response.
Routing
In Odoo, routing is managed using the @http.route decorator. This decorator tells Odoo which URL should trigger a specific method and allows you to set different options such as the type of request, authentication level, HTTP methods, and security settings.
The type parameter defines how the response is handled: for example, http is used for normal web pages or plain text responses, while json is used for API responses. The auth parameter sets who can access the route, such as public for everyone or user for logged-in users. The methods option limits which HTTP methods (like GET or POST) can be used, and settings such as csrf and cors help manage request security.
@http.route('/my/model/<int:rec_id>', type='http', auth='user')
def my_method(self, rec_id, **kwargs):
return f"Record ID: {rec_id}"Here, the <int:rec_id> part automatically converts a URL fragment into an integer argument.
Request
When a request reaches a controller, Odoo gives us a global request object that connects the HTTP layer with the Odoo environment. Using this object, developers can easily access different parts of the request.
You can read query parameters with request.params, check headers or body content using request.httprequest, and work directly with database models through request.env. The currently logged-in user’s ID can be found in request.uid.
For JSON requests, the JSON data can be accessed from request.httprequest.json. This setup makes it simple to handle incoming HTTP data and use it together with Odoo’s ORM.
Response
The value returned by a controller method determines what is sent back to the client. For routes with the type http, you can return plain text, HTML content, or even a complete Response object that includes custom headers and status codes.
For routes with the type json, you can simply return a Python dictionary or list. Odoo will automatically convert this data into a JSON response for you.
For instance, returning a custom text response can be done as follows:
from odoo.http import Response
@http.route('/custom', type='http', auth='public')
def custom(self, **kwargs):
r = Response("Custom content", status=200, mimetype='text/plain')
r.headers['X-Custom-Header'] = 'MyValue'
return r
Authentication and Security
In Odoo, the auth parameter is the main way to control access to routes. When you use auth='public', the route can be accessed by anyone. Setting auth='user' limits access only to logged-in users. You can also use auth='none' to skip session handling completely, which is helpful for external services such as webhooks.
Developers can add more security checks inside their methods, like verifying user roles or groups. For HTTP routes, CSRF protection is enabled by default, but it can be turned off when needed — for example, in JSON API routes. In those cases, it’s important to use other secure methods, such as tokens or signatures, to keep the data safe.
Cookies and Session
Odoo allows developers to manage cookies and sessions directly within controllers. You can read cookies using request.httprequest.cookies, and also add new cookies to the response when needed.
The request.session object lets you access session data, such as login information or other user-specific details. It’s important to handle sessions carefully to ensure your application remains secure and works as expected.
Error Handling
Proper error handling makes your application safer and easier to use. Controllers should return clear messages and the right HTTP status codes.
For instance, if a user tries to access something they’re not allowed to, you can return a 403 Forbidden response. If a record doesn’t exist, sending a 404 Not Found makes it clear to the client.
For JSON APIs, it’s a good idea to return well-structured error messages with helpful details, so client applications can understand and manage errors more effectively.
Here is a simple example of combining a model with a controller to build a basic To-Do API:
from odoo import http
from odoo.http import request
class TodoController(http.Controller):
@http.route('/api/todo', type='json', auth='user', methods=['GET'])
def list_todos(self, **kwargs):
todos = request.env['my.todo'].search([])
return {'todos': [{'id': t.id, 'name': t.name, 'done': t.done} for t in todos]}
@http.route('/api/todo', type='json', auth='user', methods=['POST'], csrf=False)
def create_todo(self, name=None, **kwargs):
if not name:
return {'error': 'Missing name parameter'}
todo = request.env['my.todo'].create({'name': name})
return {'todo': {'id': todo.id, 'name': todo.name, 'done': todo.done}}
@http.route('/api/todo/<int:todo_id>/toggle', type='json', auth='user', methods=['POST'], csrf=False)
def toggle_todo(self, todo_id, **kwargs):
todo = request.env['my.todo'].browse(todo_id)
if not todo.exists():
return {'error': 'Todo not found'}
todo.done = not todo.done
return {'todo': {'id': todo.id, 'done': todo.done}}
When creating controllers, it’s important to validate all incoming data to prevent errors and security issues. You should only disable CSRF protection when it’s absolutely necessary — and even then, make sure to secure those routes using tokens or digital signatures.
Always return clear error messages with the right status codes to help both developers and client applications understand what went wrong. Sensitive routes should be protected using proper authentication and access control, and the use of sudo() should be limited to reduce security risks. Good documentation for each endpoint also makes it easier for your team and external users to understand and use your API.
Overall, HTTP Controllers in Odoo 19 offer a simple yet powerful way to build APIs, integrate third-party systems, and extend the platform’s web features. They allow developers to handle frontend requests and backend logic seamlessly, while maintaining strong security and proper session management. With thoughtful design and best practices, you can create secure, efficient, and easy-to-maintain endpoints that make Odoo even more flexible and integration-friendly.
To read more about How to Create & Configure Web Controllers in Odoo 18, refer to our blog How to Create & Configure Web Controllers in Odoo 18.