{"id":262,"date":"2021-03-17T23:55:03","date_gmt":"2021-03-17T23:55:03","guid":{"rendered":"https:\/\/andrejacobs.org\/?p=262"},"modified":"2022-04-11T20:24:23","modified_gmt":"2022-04-11T20:24:23","slug":"100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api","status":"publish","type":"post","link":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/","title":{"rendered":"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API"},"content":{"rendered":"\n

Photo by Jordan Harrison<\/a> on Unsplash<\/a><\/p>\n\n\n\n

Here is my Log book<\/a><\/p>\n

Visual Studio Code or PyCharm?<\/h2>\n

Yesterday I said that I will be using PyCharm<\/a> as my Python IDE, however one part of this challenge is to get out of my comfort zone. So I have decided to give VS Code<\/a> a try as my main Python IDE for a bit.<\/p>\n

I have used VS Code here and there before but never in all out mode and certainly not for Python.<\/p>\n

The Python support needs to be installed additionally.<\/p>\n

Create new project<\/h2>\n

Up to this point none of the OpenFaaS functions I have created thus far has been stored in a git repository. I decided I will create a repo on GitHub for this new lego database project as well as any other OpenFaaS functions I work on during the challenge.<\/p>\n

I created the repo https:\/\/github.com\/andrejacobs\/learn-openfaas<\/a><\/p>\n

$ git clone git@github.com:andrejacobs\/learn-openfaas.git\n$ cd learn-openfaas\n<\/code><\/pre>\n

Build a Flask microservice with OpenFaaS<\/h3>\n

As luck would have it, OpenFaaS already has a tutorial written on how to setup Flask<\/a> using the template as well as how to deploy an existing Flask<\/a> based app.<\/p>\n

https:\/\/www.openfaas.com\/blog\/openfaas-flask\/<\/a><\/p>\n

Learning action point:<\/strong> There is a free course by the LinuxFoundation<\/a>.<\/p>\n

This made me LOLed "That\u2019s right folks. Serverless has servers."<\/p>\n

The template repo<\/a> has good info as well.<\/p>\n

Initial scouting of the above tutorial and reading the template docs makes me think I want to use the python3-http template. The nice thing about the template is that the Content-Type will be set to Content-Type: application\/json<\/code> if a dictionary is returned.<\/p>\n

The tutorial mentions that you can have RESTfull style URLs and gives a short example, but where there is a gap in my knowledge and the tutorial is where do you create this mapping? I dug a bit deeper into the linked documentation and it seems that I would need to create and deploy 1 function per API end point and would then create this mapping file to bind them all. For now this is too much trouble for what I want to achieve. (p.s. I could very easily be wrong here)<\/p>\n

However<\/strong> the tutorial also gives an example of how to deploy an existing Flask app and it shows how you can have multiple API end points from the same "function". I started going down this route but then realised there must be an easier option.<\/p>\n

Detour: Let’s explore faas-cli templates a bit<\/h3>\n

Recap, faas-cli comes with standard templates like python3 and it will pull these from the git repo<\/a> when a new function is started with faas-cli new<\/code>.<\/p>\n

Other templates like python3-flask need to be first pulled from the template store.<\/p>\n

For example asking for a new function based of the python3-http template inside of an empty directory will result in an error.<\/p>\n

$ cd ~\/temp\/template-test\n$ ls -la\n... nothing\n$ faas-cli new --lang python3-http myfunction\n2021\/03\/17 19:21:51 No templates found in current directory.\n2021\/03\/17 19:21:51 Attempting to expand templates from https:\/\/github.com\/openfaas\/templates.git\n2021\/03\/17 19:21:52 Fetched 13 template(s) : [csharp dockerfile go java11 java11-vert-x node node12 node14 php7 python python3 python3-debian ruby] from https:\/\/github.com\/openfaas\/templates.git\nPython3-http is unavailable or not supported\n\n$ ls template\ncsharp\/         java11\/         node12\/         python\/         ruby\/\ndockerfile\/     java11-vert-x\/  node14\/         python3\/\ngo\/             node\/           php7\/           python3-debian\/\n<\/code><\/pre>\n

faas-cli did however pull the standard templates into the current directory.<\/p>\n

To pull a "batteries-not-included" template.<\/p>\n

$ faas-cli template store pull python3-flask\n...\n2021\/03\/17 19:27:49 Fetched 7 template(s) : [python27-flask python3-flask python3-flask-armhf python3-flask-debian python3-http python3-http-armhf python3-http-debian] from https:\/\/github.com\/openfaas\/python-flask-template\n\n$ ls template\ncsharp\/               node\/                 python27-flask\/       python3-flask-debian\/\ndockerfile\/           node12\/               python3\/              python3-http\/\ngo\/                   node14\/               python3-debian\/       python3-http-armhf\/\njava11\/               php7\/                 python3-flask\/        python3-http-debian\/\njava11-vert-x\/        python\/               python3-flask-armhf\/  ruby\/\n\n# Create the new function from the template\n$ faas-cli new --lang python3-http myfunction\n...\nFunction created in folder: myfunction\nStack file written: myfunction.yml\n<\/code><\/pre>\n

Ok so, what happens when we run faas-cli up<\/code>?<\/p>\n

faas-cli up<\/code> is a combination of "build", "push" and "deploy"<\/p>\n

$ ls\nmyfunction\/     myfunction.yml  template\/\n\n$ faas-cli up -f myfunction.yml\n$ ls\nbuild\/          myfunction\/     myfunction.yml  template\/\n<\/code><\/pre>\n

Notice that a build directory was created.<\/p>\n

If you open the myfunction directory you will notice that you only have the handler.py and requirements.txt files. Neither of these contain the scaffolding code, in this case where is all the Flask setup and run code coming from?<\/p>\n

$ ls template\/python3-http\nDockerfile        function\/         index.py          requirements.txt  template.yml\n<\/code><\/pre>\n

The code you are looking for (SW for the win) is located in index.py.<\/p>\n

Revelation:<\/strong> The template directory is instrumental during the build phase.<\/strong> Meaning it is not just a one off clone template and modify it.<\/p>\n

# Let's proof ^^\n$ mv template\/python3-http template\/not-the-droids-you-are-looking-for\n$ faas-cli up -f myfunction.yml\n[0] > Building myfunction.\n[0] < Building myfunction done in 0.00s.\n[0] Worker done.\n\nTotal build time: 0.00s\nErrors received during build:\n- language template: python3-http not supported, build a custom Dockerfile\n\n$ cat myfunction.yml\n...\nfunctions:\n  myfunction:\n    lang: python3-http\n<\/code><\/pre>\n

Thus faas-cli<\/code> uses the lang value to determine which template to use during the build phase.<\/p>\n

Back on tour: Creating a new function using the python3-http template<\/h3>\n

After some more exploring of how I would like to build the REST apis for my learning project and lots of reading, I ended up going the path of least resistance to get my MVP out.<\/p>\n

Programmer trap:<\/strong> I was already dreaming about amazing but time consuming ways to go ahead and build a new shiny thing. But the reality is I need to focus on what I actually wanted here and that is to make my OpenFaaS function work with a database (hosted in AWS) and explore more of the features. My focus is not to build a new amazing scaffold or to write production level code (I do enough of that on the day job).<\/p>\n

Thus I will be using the python3-http template as is and just do all my API routing from the handler.py.<\/p>\n

Alex’s suggested<\/a> to just use the path and also gave a good Go example.<\/a><\/p>\n

I would however have loved to something like this in handler.py, using Flask’s routing etc.<\/p>\n

# def handle(event, context):\n#     return {\n#         "statusCode": 200,\n#         "body": "Hello from OpenFaaS!"\n#     }\n\n@app.route('\/')\ndef index():\n    return 'index'\n\n@app.route('\/login')\ndef login():\n    return 'login'\n<\/code><\/pre>\n

Maybe that is a contribution project for the future?<\/p>\n

Right, time to crack on with it.<\/p>\n

$ mkdir legodb\n$ cd legodb\n$ faas-cli template store pull python3-flask\n$ faas-cli new --lang python3-http legodb\n...\n  ___                   _____           ____\n \/ _ \\ _ __   ___ _ __ |  ___|_ _  __ _\/ ___|\n| | | | '_ \\ \/ _ \\ '_ \\| |_ \/ _` |\/ _` \\___ \\\n| |_| | |_) |  __\/ | | |  _| (_| | (_| |___) |\n \\___\/| .__\/ \\___|_| |_|_|  \\__,_|\\__,_|____\/\n      |_|\n\nFunction created in folder: legodb\nStack file written: legodb.yml\n\n$ faas-cli up -f legodb.yml\n...\nDeployed. 200 OK.\nURL: http:\/\/192.168.64.4:9999\/function\/legodb\n\n$ curl -i http:\/\/192.168.64.4:9999\/function\/legodb\nHTTP\/1.1 200 OK\nContent-Length: 20\nContent-Type: text\/html; charset=utf-8\nDate: Wed, 17 Mar 2021 16:58:47 GMT\nServer: waitress\nX-Call-Id: 137d08b0-7b90-4a87-94ea-994e24ed0a4d\nX-Duration-Seconds: 0.002660\nX-Start-Time: 1616000327192323869\n\nHello from OpenFaaS!\n<\/code><\/pre>\n

One nice thing I noticed was that running the template actually added a .gitignore<\/code> file and I don’t have to figure out what files to commit and which to ignore.<\/p>\n

\"\"<\/p>\n

Edit "legodb\/Dockerfile". I am shortcutting this a bit by looking at the python3-http template instead of just blindly following the tutorial.<\/p>\n

Initial printf style debugging<\/h2>\n

In order to understand what our handler.py function is given when a request comes in, I will be returning some values as a response (ok so I lied about printf).<\/p>\n

Looking inside of the template’s index.py you will see how the event instance is created.<\/p>\n

\"\"<\/p>\n

\"\"<\/p>\n

# Our handler.py\nimport json\n\ndef handle(event, context):\n    return {\n        'statusCode': 200,\n        'body': {\n            'headers': str(event.headers),\n            'method': str(event.method),\n            'path': str(event.path),\n            'query': str(event.query),\n            'body': str(event.body)\n        }\n    }\n<\/code><\/pre>\n

Explorer mode activated.<\/p>\n

$ faas-cli up -f legodb.yml\n$ curl -s http:\/\/192.168.64.4:9999\/function\/legodb | jq\n{\n  "body": "b''",\n  "headers": "Host: 10.62.0.68:8080\\r\\nUser-Agent: curl\/7.64.1\\r\\nAccept: *\/*\\r\\nAccept-Encoding: gzip\\r\\nX-Call-Id: 7f167e06-d757-43b1-9164-c4802d5d37bd\\r\\nX-Forwarded-For: 10.62.0.1:56786\\r\\nX-Forwarded-Host: 192.168.64.4:9999\\r\\nX-Start-Time: 1616010611817968042\\r\\n\\r\\n",\n  "method": "GET",\n  "path": "\/",\n  "query": "ImmutableMultiDict([])"\n}\n\n# Path and Query\n$ curl -s "http:\/\/192.168.64.4:9999\/function\/legodb\/abc\/xyz?param1=value1" | jq\n...\n\t"path": "\/abc\/xyz",\n  "query": "ImmutableMultiDict([('param1', 'value1')])"\n\n# Post JSON data\n$ curl -s -H "Content-Type: application\/json" -d '{"a":"apple"}' \\\n "http:\/\/192.168.64.4:9999\/function\/legodb\/abc\/xyz?param1=value1" | jq\n{\n  "body": "b'{\\"a\\":\\"apple\\"}'",\n  "headers": "... Content-Type: application\/json ...",\n  "method": "POST",\n  "path": "\/abc\/xyz",\n  "query": "ImmutableMultiDict([('param1', 'value1')])"\n}\n\n<\/code><\/pre>\n

If it is a post, can we decode from JSON to a Python dictionary? (We will need this for adding a new entry to our lego database).<\/p>\n

import json\n\ndef handle(event, context):\n    postJSON = None\n    if event.method == 'POST':\n        inputJSON = event.body.decode('utf8').replace("'", '"')\n        postJSON = json.loads(inputJSON)\n\n    return {\n        'statusCode': 200,\n        'body': {\n            'headers': str(event.headers),\n            'method': str(event.method),\n            'path': str(event.path),\n            'query': str(event.query),\n            'body': str(event.body),\n            'postJSON': postJSON\n        }\n    }\n<\/code><\/pre>\n
$ faas-cli up -f legodb.yml\n$ curl -s -H "Content-Type: application\/json" -d '{"a":"apple", "meaning":42}' \\\n "http:\/\/192.168.64.4:9999\/function\/legodb\/abc\/xyz?param1=value1" | jq\n{\n  "body": "b'{\\"a\\":\\"apple\\", \\"meaning\\":42}'",\n  "headers": ...\n  "method": "POST",\n  "path": "\/abc\/xyz",\n  "postJSON": {\n    "a": "apple",\n    "meaning": 42\n  },\n  "query": "ImmutableMultiDict([('param1', 'value1')])"\n}\n<\/code><\/pre>\n

Ok we are in a good enough place now to write the initial API handler.<\/p>\n

Hacked Handling<\/h2>\n

Hacked? why hacked? Well I am not planning on writing production level code here but just enough code to hopefully not blow up.<\/p>\n

Here is the first pass of having a mocked API.<\/p>\n

# handler.py\nimport json\n\n# GET \/legosets : Returns the list of lego sets\n# POST \/legoset : Add a new lego set to the database\n\ndef handle(event, context):\n    response = {'statusCode': 400}\n\n    if event.method == 'GET':\n        if event.path == '\/legosets':\n            legosets = get_list_of_legosets()\n            response = {'statusCode': 200, \n                'body': legosets,\n                'headers': {'Content-Type': 'application\/json'}\n                }\n    elif event.method == 'POST':\n        if event.path == '\/legoset':\n            response = add_new_legoset(event.body)\n    \n    return response\n\ndef get_list_of_legosets():\n    set1 = {'LegoID': 21322,\n        'Description': 'Pirates of Barracuda Bay', \n        'ProductURL':'',\n        'ImageURL': ''\n        }\n    return {"sets": [set1]}\n    \n\ndef add_new_legoset(body):\n    response = None\n    try:\n        inputJSON = json.loads(body.decode('utf8').replace("'", '"'))\n        response = {\n            'statusCode': 200,\n            'body': {'received': inputJSON},\n            'headers': {'Content-Type': 'application\/json'}\n        }\n    except ValueError:\n        response = {\n            'statusCode': 400,\n            'body': {'reason': 'Invalid JSON'},\n            'headers': {'Content-Type': 'application\/json'}\n         }\n    return response\n<\/code><\/pre>\n

Deploy and test various scenarios.<\/p>\n

$ faas-cli up -f legodb.yml\n\n# Invalid path test\n$ curl -i http:\/\/192.168.64.4:9999\/function\/legodb\/invalid\/path\nHTTP\/1.1 400 Bad Request\n...\n\n# Unsupported verb test\n$ curl -i -X "DELETE" http:\/\/192.168.64.4:9999\/function\/legodb\/legoset\/42\nHTTP\/1.1 400 Bad Request\n\n# Not production level worries test\n# Path is correct but has the trailing \/\n$ curl -i http:\/\/192.168.64.4:9999\/function\/legodb\/legosets\/\nHTTP\/1.1 400 Bad Request\n\n# Valid GET test\n$ curl -s http:\/\/192.168.64.4:9999\/function\/legodb\/legosets | jq\n{\n  "sets": [\n    {\n      "Description": "Pirates of Barracuda Bay",\n      "ImageURL": "",\n      "LegoID": 21322,\n      "ProductURL": ""\n    }\n  ]\n}\n\n# Post of invalid JSON test\n$ curl -s -H "Content-Type: application\/json" -d 'this is not json' "http:\/\/192.168.64.4:9999\/function\/legodb\/legoset" | jq\n{\n  "reason": "Invalid JSON"\n}\n\n# Valid POST of some JSON input test\n$ curl -s -H "Content-Type: application\/json" -d '{"a":"apple"}' \\\n "http:\/\/192.168.64.4:9999\/function\/legodb\/legoset" | jq\n{\n  "received": {\n    "a": "apple"\n  }\n}\n<\/code><\/pre>\n

\"\"<\/p>\n

Learning action point:<\/strong> I recall Alex had an example in Serverless for Everyone Else<\/a> for unit-testing a function. Need to see how I can unit-test the python based functions.<\/p>\n

Cleaning up some of the unused functions<\/h2>\n

My faasd instance is starting to get a bit crowded and I guess now is a good time to learn how to remove unused functions.<\/p>\n

$ faas-cli list\nFunction                      \tInvocations    \tReplicas\ncows                          \t1              \t1\nexpose                        \t1              \t1\nflasktest                     \t9              \t0\nhelloworld                    \t0              \t1\niss-location                  \t0              \t1\nlegodb                        \t58             \t1\nmyfunction                    \t0              \t1\nnodeinfo                      \t0              \t1\nping                          \t15             \t1\npong                          \t18             \t1\n<\/code><\/pre>\n

In Chapter 11 of Serverless for Everyone Else<\/a> there is very good information on how to clean up old docker images on your instance, how to check the resource usage of faasd, functions as well as a pruning script that you can schedule on cron.<\/p>\n

Deleting a function<\/p>\n

$ faas-cli remove cows\nDeleting: cows.\nRemoving old function.\n...\n$ faas-cli list\nFunction                      \tInvocations    \tReplicas\nlegodb                        \t58             \t1\nnodeinfo                      \t0              \t1\n<\/code><\/pre>\n

Next plans<\/h2>\n

Tomorrow I will start looking at what it would take to get SQLalchemy<\/a> setup as a dependency and connected to the MariaDB running on AWS RDS. Will also need to install mysql-connector.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"

Photo by Jordan Harrison on Unsplash […]<\/p>\n

Read more →<\/a><\/p>\n","protected":false},"author":2,"featured_media":264,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[37],"tags":[36],"yoast_head":"\n100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API - Andr\u00e9 Jacobs<\/title>\n<meta name=\"description\" content=\"Day 8 and 9 of 100 complete. I explored how the OpenFaaS templates worked and managed to get the first pass lego API working.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API - Andr\u00e9 Jacobs\" \/>\n<meta property=\"og:description\" content=\"Day 8 and 9 of 100 complete. I explored how the OpenFaaS templates worked and managed to get the first pass lego API working.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/\" \/>\n<meta property=\"og:site_name\" content=\"Andr\u00e9 Jacobs\" \/>\n<meta property=\"article:published_time\" content=\"2021-03-17T23:55:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-04-11T20:24:23+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/andrejacobs.org\/wp-content\/uploads\/2021\/03\/jordan-harrison-40XgDxBfYXM-unsplash.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1280\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Jacobs\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@andrejacobs81\" \/>\n<meta name=\"twitter:site\" content=\"@andrejacobs81\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Jacobs\" \/>\n\t<meta name=\"twitter:label2\" content=\"Estimated reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/\"},\"author\":{\"name\":\"Andr\u00e9 Jacobs\",\"@id\":\"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68\"},\"headline\":\"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API\",\"datePublished\":\"2021-03-17T23:55:03+00:00\",\"dateModified\":\"2022-04-11T20:24:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/\"},\"wordCount\":1157,\"publisher\":{\"@id\":\"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68\"},\"keywords\":[\"OpenFaaS\"],\"articleSection\":[\"100 Days Challenge\"],\"inLanguage\":\"en-GB\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/\",\"url\":\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/\",\"name\":\"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API - Andr\u00e9 Jacobs\",\"isPartOf\":{\"@id\":\"https:\/\/andrejacobs.org\/#website\"},\"datePublished\":\"2021-03-17T23:55:03+00:00\",\"dateModified\":\"2022-04-11T20:24:23+00:00\",\"description\":\"Day 8 and 9 of 100 complete. I explored how the OpenFaaS templates worked and managed to get the first pass lego API working.\",\"breadcrumb\":{\"@id\":\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/andrejacobs.org\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/andrejacobs.org\/#website\",\"url\":\"https:\/\/andrejacobs.org\/\",\"name\":\"Andr\u00e9 Jacobs\",\"description\":\"iOS Development, Electronics, Robotics, Artificial Intelligence, Business and Health\",\"publisher\":{\"@id\":\"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/andrejacobs.org\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-GB\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68\",\"name\":\"Andr\u00e9 Jacobs\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\/\/andrejacobs.org\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/andrejacobs.org\/wp-content\/uploads\/2019\/03\/andre_jacobs.jpg\",\"contentUrl\":\"https:\/\/andrejacobs.org\/wp-content\/uploads\/2019\/03\/andre_jacobs.jpg\",\"width\":450,\"height\":450,\"caption\":\"Andr\u00e9 Jacobs\"},\"logo\":{\"@id\":\"https:\/\/andrejacobs.org\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/andrejacobs.org\",\"https:\/\/www.youtube.com\/channel\/UCzqXvnd_UJ3sAzQkQBD-IVw\"],\"url\":\"https:\/\/andrejacobs.org\/author\/andre\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API - Andr\u00e9 Jacobs","description":"Day 8 and 9 of 100 complete. I explored how the OpenFaaS templates worked and managed to get the first pass lego API working.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/","og_locale":"en_GB","og_type":"article","og_title":"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API - Andr\u00e9 Jacobs","og_description":"Day 8 and 9 of 100 complete. I explored how the OpenFaaS templates worked and managed to get the first pass lego API working.","og_url":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/","og_site_name":"Andr\u00e9 Jacobs","article_published_time":"2021-03-17T23:55:03+00:00","article_modified_time":"2022-04-11T20:24:23+00:00","og_image":[{"width":1920,"height":1280,"url":"https:\/\/andrejacobs.org\/wp-content\/uploads\/2021\/03\/jordan-harrison-40XgDxBfYXM-unsplash.jpg","type":"image\/jpeg"}],"author":"Andr\u00e9 Jacobs","twitter_card":"summary_large_image","twitter_creator":"@andrejacobs81","twitter_site":"@andrejacobs81","twitter_misc":{"Written by":"Andr\u00e9 Jacobs","Estimated reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/#article","isPartOf":{"@id":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/"},"author":{"name":"Andr\u00e9 Jacobs","@id":"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68"},"headline":"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API","datePublished":"2021-03-17T23:55:03+00:00","dateModified":"2022-04-11T20:24:23+00:00","mainEntityOfPage":{"@id":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/"},"wordCount":1157,"publisher":{"@id":"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68"},"keywords":["OpenFaaS"],"articleSection":["100 Days Challenge"],"inLanguage":"en-GB"},{"@type":"WebPage","@id":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/","url":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/","name":"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API - Andr\u00e9 Jacobs","isPartOf":{"@id":"https:\/\/andrejacobs.org\/#website"},"datePublished":"2021-03-17T23:55:03+00:00","dateModified":"2022-04-11T20:24:23+00:00","description":"Day 8 and 9 of 100 complete. I explored how the OpenFaaS templates worked and managed to get the first pass lego API working.","breadcrumb":{"@id":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/andrejacobs.org\/100-days-challenge\/100-days-of-learning-day-8-9-first-pass-of-the-lego-database-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/andrejacobs.org\/"},{"@type":"ListItem","position":2,"name":"100 Days of Learning: Day 8 & 9 \u2013 First pass of the Lego database API"}]},{"@type":"WebSite","@id":"https:\/\/andrejacobs.org\/#website","url":"https:\/\/andrejacobs.org\/","name":"Andr\u00e9 Jacobs","description":"iOS Development, Electronics, Robotics, Artificial Intelligence, Business and Health","publisher":{"@id":"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/andrejacobs.org\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-GB"},{"@type":["Person","Organization"],"@id":"https:\/\/andrejacobs.org\/#\/schema\/person\/3d38360883015e883c80c2fb875c5a68","name":"Andr\u00e9 Jacobs","image":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/andrejacobs.org\/#\/schema\/person\/image\/","url":"https:\/\/andrejacobs.org\/wp-content\/uploads\/2019\/03\/andre_jacobs.jpg","contentUrl":"https:\/\/andrejacobs.org\/wp-content\/uploads\/2019\/03\/andre_jacobs.jpg","width":450,"height":450,"caption":"Andr\u00e9 Jacobs"},"logo":{"@id":"https:\/\/andrejacobs.org\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/andrejacobs.org","https:\/\/www.youtube.com\/channel\/UCzqXvnd_UJ3sAzQkQBD-IVw"],"url":"https:\/\/andrejacobs.org\/author\/andre\/"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/andrejacobs.org\/wp-content\/uploads\/2021\/03\/jordan-harrison-40XgDxBfYXM-unsplash.jpg","_links":{"self":[{"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/posts\/262"}],"collection":[{"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/comments?post=262"}],"version-history":[{"count":2,"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/posts\/262\/revisions"}],"predecessor-version":[{"id":265,"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/posts\/262\/revisions\/265"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/media\/264"}],"wp:attachment":[{"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/media?parent=262"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/categories?post=262"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/andrejacobs.org\/wp-json\/wp\/v2\/tags?post=262"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}