{"id":50126399,"url":"https://github.com/mdfarhankc/odoo_rest_api","last_synced_at":"2026-05-23T20:04:33.601Z","repository":{"id":344237814,"uuid":"1172468823","full_name":"mdfarhankc/odoo_rest_api","owner":"mdfarhankc","description":" Decorator-based REST API framework for Odoo — create clean, standardized REST endpoints with a FastAPI-like developer experience. Odoo 16+ compatible.","archived":false,"fork":false,"pushed_at":"2026-03-13T18:43:30.000Z","size":79,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-14T06:52:42.108Z","etag":null,"topics":["odoo","python","rest-api"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdfarhankc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-04T10:46:53.000Z","updated_at":"2026-03-13T18:23:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mdfarhankc/odoo_rest_api","commit_stats":null,"previous_names":["mdfarhankc/odoo_rest_api"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/mdfarhankc/odoo_rest_api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdfarhankc%2Fodoo_rest_api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdfarhankc%2Fodoo_rest_api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdfarhankc%2Fodoo_rest_api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdfarhankc%2Fodoo_rest_api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdfarhankc","download_url":"https://codeload.github.com/mdfarhankc/odoo_rest_api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdfarhankc%2Fodoo_rest_api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33410357,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T18:09:33.147Z","status":"ssl_error","status_checked_at":"2026-05-23T18:09:31.380Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["odoo","python","rest-api"],"created_at":"2026-05-23T20:04:02.416Z","updated_at":"2026-05-23T20:04:33.590Z","avatar_url":"https://github.com/mdfarhankc.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# odoo-rest-api\n\n![PyPI](https://img.shields.io/pypi/v/odoo-rest-api)\n![Python](https://img.shields.io/pypi/pyversions/odoo-rest-api)\n![License](https://img.shields.io/pypi/l/odoo-rest-api)\n\nA decorator-based REST API framework for Odoo. Create clean, standardized REST endpoints inside your Odoo modules with a FastAPI-like developer experience.\n\n## Features\n\n- **Decorator-based routing** — `@api.get()`, `@api.post()`, `@api.put()`, `@api.patch()`, `@api.delete()`\n- **Standardized JSON responses** — Consistent `{success, data, error}` format\n- **Automatic recordset serialization** — Return `env['res.partner'].search()` directly, recordsets are auto-converted to dicts\n- **Automatic request parsing** — JSON body, query params, and path params injected via signature inspection\n- **Error handling** — Exception classes map to proper HTTP status codes\n- **Pluggable authentication** — Bring your own auth logic\n- **Multi-file support** — Share one API instance across partner.py, order.py, etc.\n- **Odoo 16+ compatible**\n\n## Installation\n\n```bash\npip install odoo-rest-api\n```\n\nNo Odoo module dependency needed — just a pip package.\n\n## Quick Start\n\n### Single file\n\n```python\n# my_addon/controllers/partner_api.py\nfrom odoo_rest_api import OdooRestAPI, NotFound, BadRequest\n\napi = OdooRestAPI(prefix='/api/v1')\n\n@api.get('/partners')\ndef list_partners(env, **params):\n    limit = min(int(params.get('limit', 80)), 1000)\n    offset = int(params.get('offset', 0))\n    return env['res.partner'].search_read(\n        [], ['name', 'email', 'phone'], limit=limit, offset=offset\n    )\n\n@api.get('/partners/{id}')\ndef get_partner(env, id):\n    partner = env['res.partner'].browse(int(id))\n    if not partner.exists():\n        raise NotFound('Partner not found')\n    return partner.read(['name', 'email', 'phone'])[0]\n\n@api.post('/partners')\ndef create_partner(env, body):\n    if not body or not body.get('name'):\n        raise BadRequest(\"'name' is required\")\n    partner = env['res.partner'].create(body)\n    return partner.read(['name', 'email', 'phone'])[0]\n\n@api.put('/partners/{id}')\ndef update_partner(env, id, body):\n    partner = env['res.partner'].browse(int(id))\n    if not partner.exists():\n        raise NotFound('Partner not found')\n    partner.write(body)\n    return partner.read(['name', 'email', 'phone'])[0]\n\n@api.delete('/partners/{id}')\ndef delete_partner(env, id):\n    partner = env['res.partner'].browse(int(id))\n    if not partner.exists():\n        raise NotFound('Partner not found')\n    partner.unlink()\n    return {'deleted': True}\n\napi.register()\n```\n\n```python\n# my_addon/controllers/__init__.py\nfrom . import partner_api\n```\n\n### Multi-file (recommended)\n\nShare one API instance across multiple files:\n\n```python\n# controllers/app.py — shared instance\nfrom odoo_rest_api import OdooRestAPI\napi = OdooRestAPI(prefix='/api/v1')\n\n# controllers/partner.py\nfrom .app import api\nfrom odoo_rest_api import NotFound\n\n@api.get('/partners')\ndef list_partners(env, **params): ...\n\n@api.get('/partners/{id}')\ndef get_partner(env, id): ...\n\n# controllers/order.py\nfrom .app import api\nfrom odoo_rest_api import NotFound\n\n@api.get('/orders')\ndef list_orders(env, **params): ...\n\n@api.get('/orders/{id}')\ndef get_order(env, id): ...\n\n# controllers/__init__.py — import routes then register\nfrom . import partner\nfrom . import order\nfrom .app import api\napi.register()\n```\n\nTest:\n\n```bash\ncurl http://localhost:8069/api/v1/partners\ncurl http://localhost:8069/api/v1/partners/1\ncurl http://localhost:8069/api/v1/orders\ncurl -X POST -H \"Content-Type: application/json\" \\\n     -d '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}' \\\n     http://localhost:8069/api/v1/partners\n```\n\n## Response Format\n\n### Success\n\n```json\n{\n    \"success\": true,\n    \"data\": [{\"id\": 1, \"name\": \"Alice\", \"email\": \"alice@example.com\"}],\n    \"error\": null\n}\n```\n\n### Error\n\n```json\n{\n    \"success\": false,\n    \"data\": null,\n    \"error\": {\n        \"type\": \"NotFound\",\n        \"message\": \"Partner not found\"\n    }\n}\n```\n\n## Authentication\n\nBy 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`.\n\n### Option 1: Inline auth handler\n\n```python\nfrom odoo import SUPERUSER_ID, api as odoo_api\nfrom odoo_rest_api import OdooRestAPI, Unauthorized\n\ndef my_auth(request):\n    api_key = request.httprequest.headers.get('X-API-Key')\n    if not api_key:\n        raise Unauthorized('Missing X-API-Key header')\n\n    env = odoo_api.Environment(request.env.cr, SUPERUSER_ID, {})\n    expected = env['ir.config_parameter'].sudo().get_param('my_api.secret_key')\n\n    if api_key != expected:\n        raise Unauthorized('Invalid API key')\n\n    return SUPERUSER_ID  # or a specific user_id\n\napi = OdooRestAPI(prefix='/api/v1', auth_handler=my_auth)\n```\n\n### Option 2: Named handler (reusable across multiple APIs)\n\n```python\nfrom odoo_rest_api import register_auth_handler\n\nregister_auth_handler('my_key', my_auth)\n\napi = OdooRestAPI(prefix='/api/v1', auth='my_key')\n```\n\n### Option 3: Odoo's built-in API keys\n\n```python\nfrom odoo import SUPERUSER_ID, api as odoo_api\nfrom odoo_rest_api import OdooRestAPI, Unauthorized\n\ndef odoo_apikey_auth(request):\n    api_key = request.httprequest.headers.get('X-API-Key')\n    if not api_key:\n        raise Unauthorized('Missing X-API-Key header')\n    try:\n        env = odoo_api.Environment(request.env.cr, SUPERUSER_ID, {})\n        uid = env['res.users']._api_key_authenticate(api_key)\n    except Exception:\n        raise Unauthorized('Invalid API key')\n    return uid\n\napi = OdooRestAPI(prefix='/api/v1', auth_handler=odoo_apikey_auth)\n```\n\nSee [`examples/`](examples/) for a complete working addon with auth and multi-file routing.\n\n## Handler Signature\n\nHandler arguments are injected based on parameter names:\n\n| Parameter | Value |\n|---|---|\n| `env` | Odoo Environment (authenticated if auth handler provided) |\n| `body` | Parsed JSON request body (POST/PUT/PATCH) |\n| `{name}` matching path param | Path parameter value (e.g. `id` from `/partners/{id}`) |\n| `**params` or `**kwargs` | Remaining query string parameters |\n| Named param matching query key | Individual query parameter |\n\n## Exceptions\n\n| Exception | Status Code |\n|---|---|\n| `BadRequest` | 400 |\n| `Unauthorized` | 401 |\n| `Forbidden` | 403 |\n| `NotFound` | 404 |\n| `MethodNotAllowed` | 405 |\n| `Conflict` | 409 |\n| `ValidationError` | 422 |\n| `RateLimitExceeded` | 429 |\n\n## Recordset Serialization\n\nReturn recordsets directly — they're auto-converted to dicts via `.read()`:\n\n```python\n@api.get('/partners')\ndef list_partners(env):\n    return env['res.partner'].search([])  # Auto-serialized to list of dicts\n```\n\n## License\n\nLGPL-3.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdfarhankc%2Fodoo_rest_api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdfarhankc%2Fodoo_rest_api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdfarhankc%2Fodoo_rest_api/lists"}