{"id":16509074,"url":"https://github.com/hemanta212/flask-rest-api","last_synced_at":"2026-05-14T21:03:14.176Z","repository":{"id":104360884,"uuid":"297591483","full_name":"hemanta212/flask-rest-api","owner":"hemanta212","description":"Implementation of simple flask json REST API.","archived":false,"fork":false,"pushed_at":"2020-10-17T15:21:49.000Z","size":159,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-01T00:18:26.727Z","etag":null,"topics":["api","flask","rest"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hemanta212.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2020-09-22T08:55:03.000Z","updated_at":"2020-10-17T15:29:26.000Z","dependencies_parsed_at":"2023-04-11T11:30:52.855Z","dependency_job_id":null,"html_url":"https://github.com/hemanta212/flask-rest-api","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hemanta212/flask-rest-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hemanta212%2Fflask-rest-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hemanta212%2Fflask-rest-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hemanta212%2Fflask-rest-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hemanta212%2Fflask-rest-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hemanta212","download_url":"https://codeload.github.com/hemanta212/flask-rest-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hemanta212%2Fflask-rest-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33043249,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-14T02:00:06.663Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["api","flask","rest"],"created_at":"2024-10-11T15:48:53.117Z","updated_at":"2026-05-14T21:03:14.135Z","avatar_url":"https://github.com/hemanta212.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+TITLE: Flask REST API\n#+OPTIONS: toc:nil\n\nThis is a simple implementation of flask api with JWT(JSON WEB TOKENS). You get a token by logging in and send this token in headers for accessing every other routes.\n\n#+TOC: headlines 2\n* Table of contents :toc:\n- [[#implementation-features][Implementation Features]]\n- [[#usage][Usage]]\n  - [[#login-and-get-a-token][Login and get a token]]\n  - [[#creating-a-user][Creating a user]]\n  - [[#viewing-user-info][Viewing user info]]\n  - [[#promoting-a-user][Promoting a user]]\n  - [[#deleting-a-user][Deleting a user]]\n  - [[#creating-a-post][Creating a post]]\n  - [[#viewing-post][Viewing post]]\n  - [[#updating-a-post][Updating a post]]\n  - [[#deleting-a-post][Deleting a post]]\n- [[#configuration][Configuration]]\n  - [[#installing-dependencies][Installing dependencies]]\n  - [[#database-config][Database config]]\n  - [[#database-initialization-and-migration][Database initialization and migration]]\n  - [[#setting-up-migration-for-existing-database][Setting up migration for existing database]]\n  - [[#creating-an-admin-user][Creating an admin user]]\n- [[#application-registration-tokens][Application registration tokens]]\n- [[#loosening-other-routes][Loosening other routes]]\n- [[#useful-tools][Useful tools]]\n  - [[#httpie][Httpie]]\n  - [[#httpbinorg][Httpbin.org:]]\n  - [[#postman-and-similar-others][Postman (and similar others)]]\n- [[#inner-details][Inner details]]\n  - [[#what-requests-basichttpauth-does][What requests\" BasicHTTPAuth does]]\n  - [[#diffrent-ways-to-get-request-data-in-flask][Diffrent ways to get request data in flask]]\n- [[#todos][TODOS]]\n\n* Implementation Features\n- Rolling out custom flask decorators\n- Token based auth with jwt\n- Custom application registering and separate client tokens\n- Http Basic username:pass authentication\n- Flask blueprinting for modularization\n- Flask database migration.\n- Supports postgres, sql, sqlite etc dbs with sqlalchemy\n- Returning diffrent http status codes\n- Some common short flask tricks\n - Defining same routes with diffrent methods to reduce if/else nests\n - Returning python string, dicts autogenerates a json response (no need jsonify)\n - Calling .app_context().push() on flask app instance. Helps a ton in intrepreter to play with db and stuff without initializing\n\n* Usage\nThis api has user and post tables in db. Every routes relating to 'user' and 'post' requires a token in the header which can be obtained by logging in throgh login route.\n\nThis assumes you have completed the configuration, created admin user and have its credentials. [[#configuration][See configuration section]].\n\n\nNOTE: Any illegal or failed requests on any route will generate non-200 status code and return a json dict with one \"message\" key stating the reason\n\nExample:\n#+BEGIN_SRC json\n{\"message\": \"no user found\"}\n#+END_SRC\n\n** Login and get a token\nENDPOINT: \"login\" [GET REQ]\nPayload: auth(username, pass)\n\n#+BEGIN_SRC python\nimport requests\nfrom requests.auth import HTTPBasicAuth\n\nURL = \"https://apiurl.com\"\n# fill username and password with your acc info\nauth = HTTPBasicAuth(\"username\", \"password\")\n\nlogin_response = requests.get(URL+\"/login\", auth=auth)\nlogin_response.status_code # confirm that its 200\n\ntoken = login_response.json()[\"token\"]\n\nheaders = {\n    \"x-access-token\": token\n}\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"token\": \"soasloiwurpoewiurpowierupwoeirf\"}\n#+END_SRC\n\nFor accessing every other route, you need to specify this token in the header as value of \"x-access-token\".\n\nNOTE: This token has no time limit and will never expire, [[#application-registration-tokens][see why.]] For making expirable tokens have a look at [[https://pyjwt.readthedocs.io/en/latest/usage.html#expiration-time-claim-exp][here]].\n\n** Creating a user\nENDPOINT: \"user\" [POST REQ]\nREQ: admin token\n\nCreating a user requires admin account\"s token. You send a post request to the \"user\" endpoint.\n\nThe payload should have username and password.\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\ndata = {\"username\": \"some usename\", \"password\": \"my password\"}\n\ncreate_user = requests.post(URL+\"/user\", data=data, headers=headers\n#+END_SRC\n\nResponse: 200\n#+BEGIN_SRC json\n{\n    \"message\": \"User created successfully\"\n}\n#+END_SRC\n\n** Viewing user info\nENDPOINT: \"user\" [GET REQ]\nREQ: admin token\n\nSending the request gets you all users info\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\nrequests.get(URL+\"/user\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"users\": [\n    {\"admin\": true, \"id\": 1,\n     \"password\": \"sha256$Sot2tcp9$671301dae8s45ad6f2fe0f583f8e60bfc90b24f045fcb791c4483711ca9c6d09\",\n     \"public_id\": \"e9572ee6-4b5e-45e4-a840-58a33b04b8a7\",\n     \"username\": \"my username\"}\n   ]\n}\n#+END_SRC\n\n**** Viewing Single User\nENDPOINT: \"user/public_id\" [GET REQ]\nREQ: admin token\n\nYou can get public id of user by sending GET req to \"user\" endpoint: see above\n#+BEGIN_SRC python\nrequests.get(URL+\"/user/public_id\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"user\":\n {\n    \"admin\": false,\n     \"id\": 2,\n     \"password\": \"sha256$f8ulwnAv$8af6f5590e8af54c8d2171cc9afc568727a8a763e8c875855f8b7d27f5dfcccd\",\n     \"public_id\": \"1f190b06-263s-42aa-86e9-460d0aff93d9\",\n     \"username\": \"my username\"\n }\n}\n#+END_SRC\n\n** Promoting a user\nENDPOINT: \"user/public_id\" [PUT REQ]\nREQ: admin token\n\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\nrequests.put(URL+\"/user/public_id\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"message\": \"The user has been promoted!\"}\n#+END_SRC\n\n** Deleting a user\nENDPOINT: \"user/public_id\" [DELETE REQ]\nREQ: admin token\n\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\nrequests.delete(URL+\"/user/public_id\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"message\": \"The user has been deleted!\"}\n#+END_SRC\n\n** Creating a post\nENDPOINT: \"template\" [POST REQ]\n\nThe payload should have title and url and optionally description.\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\ndata = {\"title\": \"some title\",\n         \"url\": \"http:/test.com\",\n         \"description\": \"some desc\",\n         }\nrequests.put(URL+\"/user/public_id\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"message\": \"Post created\"}\n#+END_SRC\n** Viewing post\nENDPOINT: \"template\" [GET REQ]\n\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\nrequests.get(URL+\"/template\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"templates\": [\n    {\"description\": \"Done\",\n     \"id\": 27,\n     \"posted\": \"Mon, 12 Oct 2020 04:51:27 GMT\",\n     \"title\": \"Test thing\",\n     \"url\": \"https://i.imgur.com/yYGxFJX.jpeg\",\n     \"username\": \"somerandomusername\",\n     \"posted\": true},\n\n    {\"description\": null,\n     \"id\": 27,\n     \"posted\": \"Mon, 12 Oct 2020 04:51:27 GMT\",\n     \"title\": \"Test thing\",\n     \"url\": \"https://i.imgur.com/yYGxFJX.jpeg\",\n     \"username\": null,\n     \"posted\": false},   ]\n}\n#+END_SRC\nNote: Sometimes user_id, description can be null.\n\n*** View filtered post\nENDPOINT: \"/\" [GET REQ]\n\nThe api provides a way to get approved post (with approved propery set to true + current user's own post) with a single api call.\n#+BEGIN_SRC python\nrequests.get(URL+\"/\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"templates\": [\n    {\"description\": \"Done\",\n     \"id\": 27,\n     \"posted\": \"Mon, 12 Oct 2020 04:51:27 GMT\",\n     \"title\": \"Test thing\",\n     \"url\": \"https://i.imgur.com/yYGxFJX.jpeg\",\n     \"username\": \"somerandomusername\",\n     \"posted\": true}\n   ]\n}\n#+END_SRC\nNote: Sometimes user_id, description can be null too.\n\n*** Viewing Single Post\nENDPOINT: \"template/template_id\" [GET REQ]\n\nYou can get template id of post by sending GET req to \"template\" endpoint: see above\n#+BEGIN_SRC python\nrequests.get(URL+\"/template/template_id\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"template\":\n {\"description\": \"Done\",\n     \"id\": 27,\n     \"posted\": \"Mon, 12 Oct 2020 04:51:27 GMT\",\n     \"title\": \"Test thing\",\n     \"url\": \"https://i.imgur.com/yYGxFJX.jpeg\",\n     \"user_id\": \"alskjdf_dfkdjf\"\n }\n}\n#+END_SRC\nNote: Sometimes user_id, description can be null too.\n\n** Updating a post\nENDPOINT: \"template/template_id\" [PUT REQ]\n\nUpdating a post is same as creating it.\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\ndata = {\"title\": \"some title\",\n         \"url\": \"http:/test.com\",\n         \"description\": \"some desc\",\n         }\nrequests.put(URL+\"/template/template_id\", data=data, headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"message\": \"Post Updated\"}\n#+END_SRC\n\n** Deleting a post\nENDPOINT: \"template/template_id\" [DELETE REQ]\n\n#+BEGIN_SRC python\nheaders = {\"x-access-token\": token}\nrequests.delete(URL+\"/template/template_id\", headers=headers)\n#+END_SRC\n\nRESPONSE: 200\n#+BEGIN_SRC json\n{\"message\": \"The post has been deleted\"}\n#+END_SRC\n\n* Configuration\nAll the configs are set in the meme_api/__init__.py file.\n\n** Installing dependencies\n- With Pip\n #+BEGIN_SRC shell\n $ python3 -m venv .venv\n $ .venv/bin/python -m pip install -r requirements.txt\n #+END_SRC\n- With Poetry\n #+BEGIN_SRC shell\n $ poetry install\n #+END_SRC\n** Database config\n  The config SQLALCHEMY_DATABASE_URI is made from different env vars parts like HOST_NAME, HOST_PASS etc You need to set those variables\n  Or you can just use sqlite db.\n\n  A minimal '.env' config looks like\n  #+BEGIN_SRC shell\n  export SECRET_KEY='mysecretkey'\n  export SQLALCHEMY_DATABASE_URI='sqlite:///site.db'\n  export FLASK_APP=run.py\n  #+END_SRC\n\n  This same config along with example config for hosted sql (eg MYSQL) server is available in .env_eg file. Just rename, edit and source this file.\n  #+BEGIN_SRC shell\n   #+ .env_eg file +#\n   export SECRET_KEY='mysecretkey'\n   export SQLALCHEMY_DATABASE_URI='sqlite:///site.db'\n   export FLASK_APP=run.py\n\n   # For a hosted mysql/postgres server\n   # Note: if SQLALCHEMY_DATABASE_URI env var is present these env vars will be ignored \u0026 WONT BE USED\n   export DB_USERNAME='username of database'\n   export DB_PASS='password of database'\n   export DB_HOST='host address url of database'\n   export DB_NAME='name of db and tablename eg. mysqldb$posts'\n  #+END_SRC\n** Database initialization and migration\nBefore initializing the database. Create a migrations folder for you db and delete the existing one\n#+BEGIN_SRC shell\n$ rm -rf ./migrations\n$ python -m flask db init # makes migrations folder\n#+END_SRC\n\nRun migrate to create the tables required by the models\n#+BEGIN_SRC\n$ python -m flask db migrate\n$ python -m flask db upgrade\n#+END_SRC\n\nOnce you make any changes to models you need to migrate \u0026 upgrade the database as shown above\n\n** Setting up migration for existing database\nIn case you already have a database initialized(ie db schema created) through different option and want to integrate flask-migrate in it.\n\nFirst: Initialize the migrations folder\nNote: delete existing migrations folder\n#+BEGIN_SRC shell\n$ python -m flask db init\n#+END_SRC\n\nCreate another empty database table and point the database env variables to this empty table (in case of sqlite just change the 'site.db' name to 'site2.db')\n\n#+BEGIN_SRC shell\n$ python -m flask db migrate\n#+END_SRC\n\nNow again point to your original database column in environment vars (for sqlite just change 'site2.db' back to 'site.db')\n\n#+BEGIN_SRC shell\n$ python -m flask db stamp head\n$ python -m flask db migrate # you should see 'no change in schema detected' message\n#+END_SRC\n\nYou are all set. From now, if you make any changes to models you need to migrate \u0026 upgrade the database as shown below\n#+BEGIN_SRC\n$ python -m flask db migrate\n$ python -m flask db upgrade\n#+END_SRC\n\n** Creating an admin user\nOnly admin users are allowed to create new accounts through api. Thus a admin user has to be manually created (or you could remove that if statement and create user acc through that route)\n#+BEGIN_SRC python\nimport uuid\n\nfrom werkzeug.security import generate_password_hash\n\nfrom run import app\nfrom meme_api import db\nfrom meme_api.models import User\n\napp.app_context().push()\n\nhashed_pass = generate_password_hash('secretpassword', method='sha256')\n\nadmin = User(username='admin',\n             password=hashed_pass,\n             admin=True,\n             public_id=str(uuid.uuid4()) )\n\ndb.session.add(admin)\ndb.session.commit()\n#+END_SRC\n\n* Application registration tokens\nThe token generated by the api never expires. For preventing leaked tokens to be misued and also limit the database connections, the prod branch of this repo implements a application based registering.\n\nA random uuid is generated and manually put into the meme_api/apps.py file. This id can now be used in headers for requesting every route.\n#+BEGIN_SRC python\n#+ apps.py file +\nregistered = {\n    'someapp': 'generated random uuid',\n    'cli': 'another uuid for another app',\n}\n#+END_SRC\n\n#+BEGIN_SRC python\nheaders = {\n    'x-application-token': 'uuid token for application',\n    'x-access-token': 'user login token',\n}\n#+END_SRC\nEvery routes including login now requires above 'x-application-token' header for the request to be successful.\n\n* Loosening other routes\nWith application based authentication in place, the routes for creating new user, getting all users etc can be loosened to not require an admin token.\n\n* Useful tools\nThere are many good tools to leverage understanding of how api's and http requests work.\n** [[https://github.com/httpie/httpie][Httpie]]\n- CLI tools for testing, debugging API endpoints.\n** Httpbin.org:\n- An dedicated website which provides post, delete, put etc endpoints in httpbin.org/post, /delete respectivly. Returns all the headers and data info it got in nice json format.\n - Great partner tool with httpie\n\n** Postman (and similar others)\n- Exploring, testing endpoints with diffrent kinds of requests in a friendly UI. Helps creating a test suite.\n\n* Inner details\n** What requests\" BasicHTTPAuth does\n#+BEGIN_SRC python\nimport requests\nfrom requests.auth import HTTPBasicAuth\n\nURL = \"https://httpbin.org\"\nauth = HTTPBasicAuth(\"username\", \"password\")\n\nlogin_response = requests.post(URL+\"/post\", auth=auth)\n\nprint(login_response.json())\n#+END_SRC\n\nResponse\n#+BEGIN_SRC json\n{\"args\": {},\n \"data\": \"\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\"Accept\": \"*/*\",\n             \"Accept-Encoding\": \"gzip, deflate\",\n             \"Authorization\": \"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\",\n             \"Content-Length\": \"0\",\n             \"Host\": \"httpbin.org\",\n             \"User-Agent\": \"python-requests/2.24.0\",\n             \"X-Amzn-Trace-Id\": \"Root=1-5f8aee35-211905107cfea23a2ad3b865\"},\n \"json\": null,\n \"origin\": \"35.229.170.146\",\n \"url\": \"https://httpbin.org/post\"}\n #+END_SRC\n\n What we are interested in is the Authorization header. Basically the requests transformed the username and password to base64 encoded string and passed the header.\n #+BEGIN_SRC python\n header = {\n     \"Authorization\": \"Basic \" + Base64encoded(username + \":\" + password)\n }\n #+END_SRC\n\n So instead of passing auth arg we can also create this authorization header ourself and should get the same result\n\n*** Implementing own auth header\n#+BEGIN_SRC python\nimport requests\nimport base64\n\nURL = \"httpbin.org/post\"\ntoken = base64.b64encode(bytes(\"username:pass\", \"utf-8\"))\nheaders  = {\"Authorization\": f\"Basic {token.decode()}\"}\nresponse = requests.get(URL, headers=headers)\n\nprint(response.json())\n#+END_SRC\n#+BEGIN_SRC json\n{\"args\": {},\n \"data\": \"\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\"Accept\": \"*/*\",\n             \"Accept-Encoding\": \"gzip, deflate\",\n             \"Authorization\": \"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\",\n             \"Content-Length\": \"0\",\n             \"Host\": \"httpbin.org\",\n             \"User-Agent\": \"python-requests/2.24.0\",\n             \"X-Amzn-Trace-Id\": \"Root=1-5f8af1bb-716f15011a1b61770e118a7f\"},\n \"json\": null,\n \"origin\": \"35.229.170.146\",\n \"url\": \"https://httpbin.org/post\"}\n#+END_SRC\n\n** Diffrent ways to get request data in flask\nRef: [[https://stackoverflow.com/questions/10434599/get-the-data-received-in-a-flask-request][stackoverflow page]]\n\n- request.data : used for fallback data storage mostly empty\n\n- request.args: the key/value pairs in the URL query string\n\n- request.form:\n   the key/value pairs in the body, from a HTML post form, or JavaScript request that isn't JSON encoded\n\n- request.files:\n  the files in the body, which Flask keeps separate from form. HTML forms must\n use enctype=multipart/form-data or files will not be uploaded.\n\n- request.values:\n   combined args and form, preferring args if keys overlap\n\n- request.json:\n  parsed JSON data. The request must have the application/json content type, or\n  use request.get_json(force=True) to ignore the content type.\n\n* TODOS\n**** [ ] Add Tests\n**** [ ] Add Logging\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhemanta212%2Fflask-rest-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhemanta212%2Fflask-rest-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhemanta212%2Fflask-rest-api/lists"}