Decorator-based REST API framework for Odoo modules
Project description
odoo-rest-api
A decorator-based REST API framework for Odoo. Create clean, standardized REST endpoints inside your Odoo modules with a FastAPI-like developer experience.
Features
- Decorator-based routing —
@api.get(),@api.post(),@api.put(),@api.patch(),@api.delete() - Standardized JSON responses — Consistent
{success, data, error}format - Automatic recordset serialization — Return
env['res.partner'].search()directly, recordsets are auto-converted to dicts - Automatic request parsing — JSON body, query params, and path params injected via signature inspection
- Error handling — Exception classes map to proper HTTP status codes
- Pluggable authentication — Bring your own auth logic
- Multi-file support — Share one API instance across partner.py, order.py, etc.
- Odoo 16+ compatible
Installation
pip install odoo-rest-api
No Odoo module dependency needed — just a pip package.
Quick Start
Single file
# my_addon/controllers/partner_api.py
from odoo_rest_api import OdooRestAPI, NotFound, BadRequest
api = OdooRestAPI(prefix='/api/v1')
@api.get('/partners')
def list_partners(env, **params):
limit = min(int(params.get('limit', 80)), 1000)
offset = int(params.get('offset', 0))
return env['res.partner'].search_read(
[], ['name', 'email', 'phone'], limit=limit, offset=offset
)
@api.get('/partners/{id}')
def get_partner(env, id):
partner = env['res.partner'].browse(int(id))
if not partner.exists():
raise NotFound('Partner not found')
return partner.read(['name', 'email', 'phone'])[0]
@api.post('/partners')
def create_partner(env, body):
if not body or not body.get('name'):
raise BadRequest("'name' is required")
partner = env['res.partner'].create(body)
return partner.read(['name', 'email', 'phone'])[0]
@api.put('/partners/{id}')
def update_partner(env, id, body):
partner = env['res.partner'].browse(int(id))
if not partner.exists():
raise NotFound('Partner not found')
partner.write(body)
return partner.read(['name', 'email', 'phone'])[0]
@api.delete('/partners/{id}')
def delete_partner(env, id):
partner = env['res.partner'].browse(int(id))
if not partner.exists():
raise NotFound('Partner not found')
partner.unlink()
return {'deleted': True}
api.register()
# my_addon/controllers/__init__.py
from . import partner_api
Multi-file (recommended)
Share one API instance across multiple files:
# controllers/app.py — shared instance
from odoo_rest_api import OdooRestAPI
api = OdooRestAPI(prefix='/api/v1')
# controllers/partner.py
from .app import api
from odoo_rest_api import NotFound
@api.get('/partners')
def list_partners(env, **params): ...
@api.get('/partners/{id}')
def get_partner(env, id): ...
# controllers/order.py
from .app import api
from odoo_rest_api import NotFound
@api.get('/orders')
def list_orders(env, **params): ...
@api.get('/orders/{id}')
def get_order(env, id): ...
# controllers/__init__.py — import routes then register
from . import partner
from . import order
from .app import api
api.register()
Test:
curl http://localhost:8069/api/v1/partners
curl http://localhost:8069/api/v1/partners/1
curl http://localhost:8069/api/v1/orders
curl -X POST -H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "john@example.com"}' \
http://localhost:8069/api/v1/partners
Response Format
Success
{
"success": true,
"data": [{"id": 1, "name": "Alice", "email": "alice@example.com"}],
"error": null
}
Error
{
"success": false,
"data": null,
"error": {
"type": "NotFound",
"message": "Partner not found"
}
}
Authentication
By default, routes have no authentication (auth="none"). You add auth by providing your own handler — a function that takes request and returns a user_id.
Option 1: Inline auth handler
from odoo import SUPERUSER_ID, api as odoo_api
from odoo_rest_api import OdooRestAPI, Unauthorized
def my_auth(request):
api_key = request.httprequest.headers.get('X-API-Key')
if not api_key:
raise Unauthorized('Missing X-API-Key header')
env = odoo_api.Environment(request.env.cr, SUPERUSER_ID, {})
expected = env['ir.config_parameter'].sudo().get_param('my_api.secret_key')
if api_key != expected:
raise Unauthorized('Invalid API key')
return SUPERUSER_ID # or a specific user_id
api = OdooRestAPI(prefix='/api/v1', auth_handler=my_auth)
Option 2: Named handler (reusable across multiple APIs)
from odoo_rest_api import register_auth_handler
register_auth_handler('my_key', my_auth)
api = OdooRestAPI(prefix='/api/v1', auth='my_key')
Option 3: Odoo's built-in API keys
from odoo import SUPERUSER_ID, api as odoo_api
from odoo_rest_api import OdooRestAPI, Unauthorized
def odoo_apikey_auth(request):
api_key = request.httprequest.headers.get('X-API-Key')
if not api_key:
raise Unauthorized('Missing X-API-Key header')
try:
env = odoo_api.Environment(request.env.cr, SUPERUSER_ID, {})
uid = env['res.users']._api_key_authenticate(api_key)
except Exception:
raise Unauthorized('Invalid API key')
return uid
api = OdooRestAPI(prefix='/api/v1', auth_handler=odoo_apikey_auth)
See examples/ for a complete working addon with auth and multi-file routing.
Handler Signature
Handler arguments are injected based on parameter names:
| Parameter | Value |
|---|---|
env |
Odoo Environment (authenticated if auth handler provided) |
body |
Parsed JSON request body (POST/PUT/PATCH) |
{name} matching path param |
Path parameter value (e.g. id from /partners/{id}) |
**params or **kwargs |
Remaining query string parameters |
| Named param matching query key | Individual query parameter |
Exceptions
| Exception | Status Code |
|---|---|
BadRequest |
400 |
Unauthorized |
401 |
Forbidden |
403 |
NotFound |
404 |
MethodNotAllowed |
405 |
Conflict |
409 |
ValidationError |
422 |
RateLimitExceeded |
429 |
Recordset Serialization
Return recordsets directly — they're auto-converted to dicts via .read():
@api.get('/partners')
def list_partners(env):
return env['res.partner'].search([]) # Auto-serialized to list of dicts
License
LGPL-3.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file odoo_rest_api-0.2.1.tar.gz.
File metadata
- Download URL: odoo_rest_api-0.2.1.tar.gz
- Upload date:
- Size: 22.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab100663c901dc722d65434b5c9e4e9bde599276065fd666cca0dfd1974cdc67
|
|
| MD5 |
d785fbb24c0d2ec8e5f2cca833a80515
|
|
| BLAKE2b-256 |
04d919b7a9f5348f89471a4a1a5803ede5a22b82623242dd1b2b0f7c93f2bf49
|
File details
Details for the file odoo_rest_api-0.2.1-py3-none-any.whl.
File metadata
- Download URL: odoo_rest_api-0.2.1-py3-none-any.whl
- Upload date:
- Size: 17.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
baf8024d7064b261af6b6b048551a580fae4d1a1677bdebfdd18a3344621f1cb
|
|
| MD5 |
a99faa5ef53dceac5e62c2937c6ba615
|
|
| BLAKE2b-256 |
8a124de6add480504db1349332e395de4da97e444bcf3b030f494c38ab5ca6b1
|