{"id":379,"date":"2025-09-02T13:12:19","date_gmt":"2025-09-02T12:12:19","guid":{"rendered":"https:\/\/guillaumesblog.net\/?p=379"},"modified":"2025-09-02T13:48:05","modified_gmt":"2025-09-02T12:48:05","slug":"3-tier-webapp-on-oci","status":"publish","type":"post","link":"https:\/\/guillaumesblog.net\/index.php\/3-tier-webapp-on-oci\/","title":{"rendered":"Connecting Frontend, API, and Database on OCI: 3-Tier Architecture in Practice"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">More cloud fun here with a 3 tier Webapp and use of cloud native services, let&#8217;s get started and have a look at the architecture I propose<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2511\" height=\"1791\" src=\"https:\/\/guillaumesblog.net\/wp-content\/uploads\/2025\/09\/3tier-with_a_bucket.drawio.png\" alt=\"\" class=\"wp-image-403\"\/><\/figure>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\">We have a presentation layer made out of HTML &amp; JS stored in a Bucket that you access through the API gateway as a static website. I follow the method described <a href=\"https:\/\/medium.com\/oracledevs\/how-to-serve-website-static-files-from-the-oci-object-storage-bd79ca0805c7\" data-type=\"link\" data-id=\"https:\/\/medium.com\/oracledevs\/how-to-serve-website-static-files-from-the-oci-object-storage-bd79ca0805c7\">here<\/a>, it is straight forward with one remark though, which is to not forget to change the metadata of the file being uploaded to the bucket with Content-Type: text\/html; otherwise when you access the URL files will get downloaded instead of being displayed by the browser, see below. For some reason all objects uploaded by me on the bucket get the default value Content-Type: application\/octet-stream which causes the issue.<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1171\" height=\"750\" src=\"https:\/\/guillaumesblog.net\/wp-content\/uploads\/2025\/09\/bucket2.png\" alt=\"\" class=\"wp-image-388\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Once done with the previous step your website can just be fetched and now runs from your browser. Your browser then talks to the API gateway again but through a different &#8220;deployment&#8221; inside the API gateway, which is a logical way of grouping APIs and Routes. I had some difficulties with routes figuring out CORS Origins, allowed methods and headers and as such I recommend that you start with a wildcard in these for a lab &#8211; if like me you are sure of what you are doing. Please note In production you should never ever leave wildcards \u2014 always restrict origins and methods. Also, I recommend you use the browser in development mode, as it will help you in the debugging process. <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1906\" height=\"852\" src=\"https:\/\/guillaumesblog.net\/wp-content\/uploads\/2025\/09\/cors0.png\" alt=\"\" class=\"wp-image-393\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The API gateway then redirects requests to my python back end on a private subnet running a Flask APP &#8211; code <a href=\"https:\/\/github.com\/geddegda\/3tier-webapp\/blob\/main\/backend\/app.py\">here<\/a> &#8211; the objective is to map each of your OCI gateway routes to the flask app routes on the back end e.g. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@app.route(\"\/update_product\", methods=&#91;\"POST\"])<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, the python code runs SQL commands and reaches MySQL on another private subnet where the database is located, the idea is to have a well segmented architecture in order to be able to make better sense of security and firewall rules. I assume on this blog that you have a good understanding of security lists on OCI and firewall on Linux which you will have to deal with.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we imagine this app a personal app or mock-up, and think now in terms of backup strategy, I suggest to use volume clones for the backend VM and some automated backups on MySQL which are all easily managed from OCI.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On a side note, what does my wonderful app do? Two things, you can register products and list their prices &#8211; and that&#8217;s it &#8211; of course it\u2019s not advanced \u2014 the real point is the layered architecture \ud83d\ude09 If you want to copy the code the repo is <a href=\"https:\/\/github.com\/geddegda\/3tier-webapp\" data-type=\"link\" data-id=\"https:\/\/github.com\/geddegda\/3tier-webapp\">here<\/a>, and you can always make a pull request if you feel like it. \ud83d\ude42<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"991\" height=\"478\" src=\"https:\/\/guillaumesblog.net\/wp-content\/uploads\/2025\/09\/webapp0.png\" alt=\"\" class=\"wp-image-391\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If we come back to the architectural topic, originally I was not aware of the trick of having a combination of API gateway and Object storage to store my static website and  this is what I had in mind before optimisation.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2666\" height=\"1795\" src=\"https:\/\/guillaumesblog.net\/wp-content\/uploads\/2025\/09\/3tier-with_an_instance.drawio.png\" alt=\"\" class=\"wp-image-404\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Downside would be that you would have to pay for the Frontend machine and Load balancer, which we would probably want to avoid.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally one last thing, I use the vault service to import secrets in my code back-end in order to keep the secret, secret, like so:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def get_secret():\n    ##blabla...\n    encoded_secret = response.data.secret_bundle_content.content\n    secret = base64.b64decode(encoded_secret).decode(\"utf-8\")\n    return secret<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The goal here is to show how cloud native services can be combined into a simple, secure 3-tier architecture. I hope you enjoyed it and talk soon!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>More cloud fun here with a 3 tier Webapp and use of cloud native services, let&#8217;s get started and have a look at the architecture I propose<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-379","post","type-post","status-publish","format-standard","hentry","category-conversation"],"_links":{"self":[{"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/posts\/379","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/comments?post=379"}],"version-history":[{"count":21,"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/posts\/379\/revisions"}],"predecessor-version":[{"id":409,"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/posts\/379\/revisions\/409"}],"wp:attachment":[{"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/media?parent=379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/categories?post=379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/guillaumesblog.net\/index.php\/wp-json\/wp\/v2\/tags?post=379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}