{"id":13033,"date":"2022-04-11T08:56:35","date_gmt":"2022-04-11T15:56:35","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=13033"},"modified":"2025-06-13T21:25:00","modified_gmt":"2025-06-14T04:25:00","slug":"build-a-python-microservice-with-couchbase-part-2","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/","title":{"rendered":"Build A Python Microservice With Couchbase \u2013 Part 2"},"content":{"rendered":"<p>In <a href=\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-1\/\" target=\"_blank\" rel=\"noopener\">the first installment of this series<\/a>, we discussed the drivers behind creating microservices, and why Couchbase is the perfect datastore to use with a microservice architecture. With their stateless nature, they can be deployed anywhere and horizontally scaled as needed. While you could write a microservice in any language, to fit into an agile workflow where you need to deliver functionality fast, you should choose a language known by most developers and allow for rapid development. Since Python and JavaScript are two popular languages, either would be a good fit. In this blog series, we are focusing on Python.<\/p>\n<h2>Distributed performance considerations<\/h2>\n<p>Application performance requirements are important, but they are sometimes difficult to quantify. A simple example is a report that runs in X hours; however, it should run in Y minutes due to business requirements. That example is easy to quantify and provides a clear target for improvement. When you have a geographically distributed architecture, if a component can support X requests per second, that does not mean anything. Considering the number of variables in a geographically <a href=\"https:\/\/www.couchbase.com\/blog\/distributed-applications\/\">distributed application<\/a>, you need to take a step back and start with something you can quantify; for example, you want a smartphone app to fully load in X seconds, or you want a web page to fully load in Y seconds. Then work backward to see what is needed to make that happen.<\/p>\n<h2>Python microservice performance considerations<\/h2>\n<p>When you create a microservice in Python, you have some options to consider before you start coding. You can either write all your own Python code to create the service or use a Python API framework such as Flask or FastAPI. In <a href=\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-1\/\" target=\"_blank\" rel=\"noopener\">the first installment of this series<\/a>, I provided an example of the first option. I will call this the &#8220;full code&#8221; option. In this second part of the series, I will introduce an implementation of the simple User Profile service using FastAPI. I chose FastAPI over Flask for this Blog because most consider it faster, and I thought it would be fun to give it a try.<\/p>\n<p>But first, let&#8217;s focus on our original &#8220;full code&#8221; example. We used Python&#8217;s <em>HTTPServer<\/em> class to create a basic Web Server to respond to our API calls. For our API, we decided to use paths (as opposed to parameters or posting a JSON body) as it is fast and easy to parse. Our simple User Profile API does not need to provide much \u2013 just a few methods to look up a user profile and get the data. I included options for lookup by <em>ID<\/em>, <em>Nickname<\/em> or <em>Username<\/em>. In real life, requirements will vary based on how the upstream application is designed.<\/p>\n<p>The full code program has two logical areas \u2013 code that is executed once and the code executed for each request \u2013 namely the do_GET function. For expedience, we won&#8217;t focus on the limited execution code, but we will focus on the do_GET function and its satellite functions. With Python&#8217;s <em>HTTPServer<\/em> class, this function will be called with each request. The request path will be in the class and accessible via <em>self.path<\/em> and the headers are in <em>self.headers<\/em>. If you are just getting started with Python, <em>self<\/em> is like this in Java \u2013 it references the calling instance of the class.<\/p>\n<p>The service will need to iterate on the contents of the path string so it can do the appropriate lookup and return the data. Thanks to the beauty of Couchbase&#8217;s JSON native design, we don&#8217;t have to do much, if anything, to the data before we send it. So, we will focus on how to inspect the path. Python has a lot of built-in options for string processing which enable you to write pretty code, but not necessarily the fastest code. Python is an interpreted language (it runs directly from the source) so statements make a difference.<\/p>\n<p>Let&#8217;s look at two options for path string processing &#8211; the <em>startswith<\/em> and <em>split<\/em> methods.<\/p>\n<pre class=\"decode-attributes:false lang:python decode:true \">% python3 -m timeit -s 'text=\"\/api\/v1\/id\/4\"' 'text.startswith(\"\/api\/v1\/id\/\")'\r\n2000000 loops, best of 5: 111 nsec per loop\r\n\r\n% python3 -m timeit -s 'text=\"\/api\/v1\/id\/4\"' 'text.split(\"\/\")[-1]'\r\n1000000 loops, best of 5: 205 nsec per loop<\/pre>\n<p>The split is more expensive, but we are going to have to do it, so it would be best to do it only once. We can then avoid calling anything else by using the array returned from the split as opposed to <em>startswith<\/em>.<\/p>\n<pre class=\"decode-attributes:false lang:python decode:true\">% python3 -m timeit -s 'True if 1 == 5 else False'\r\n50000000 loops, best of 5: 6.07 nsec per loop<\/pre>\n<p>Conditional statements are fast, so while it may not look pretty, we will do a single split and then build an <em>if\u2026elif\u2026else<\/em> structure to iterate through the path. We will write short helper functions to do a query or key-value get and return the JSON data to the requestor with minimal processing.<\/p>\n<p>Also, to make our microservice secure, we will add a Bearer Token. We would use something like OAuth with Bearer and JWT tokens in a real environment. For our example, we will greatly simplify this and add a collection to our schema with a fixed token. The service will query this token on startup and only respond to requests that provide this token as a Bearer token. Finally, if needed, we will add a health check path that responds with an <em>HTTP 200<\/em> so we know our service is healthy.<\/p>\n<pre class=\"decode-attributes:false lang:python decode:true\"> \u00a0\u00a0 def do_GET(self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 path_vector = self.path.split('\/')\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 path_vector_length = len(path_vector)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if path_vector_length == 5 and path_vector[3] == 'id':\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if not self.v1_check_auth_token(self.headers):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.unauthorized()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 request_parameter = path_vector[4]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 records = self.v1_get_by_id('user_data', request_parameter)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.v1_responder(records)<\/pre>\n<h2>Containerizing the Python microservice<\/h2>\n<p>I decided to use Kubernetes to test the service, so I had to build containers with the various implementations of the API. There is a published Python container that can be used as a base. Some OS prerequisites will have to be installed before the required Python packages. The Python container is based on Debian so the prerequisite packages can be installed with APT. Then <em>pip<\/em> can be called to install the required Python packages. The service port will need to be exposed, and finally, the service can be run as it would be run from the command line. To containerize the service, it will need an additional modification to support environment variables as this is the preferred method for passing parameters into a container.<\/p>\n<p>This is an example of the Dockerfile for the full code service:<\/p>\n<pre class=\"decode-attributes:false lang:sh decode:true\">FROM python:3.9-bullseye\r\nRUN apt update\r\nRUN apt install elpa-magit -y\r\nRUN apt install git-all python3-dev python3-pip python3-setuptools cmake build-essential libssl-dev -y\r\nWORKDIR \/usr\/src\/app\r\nADD . \/usr\/src\/app\r\nRUN pip install --no-cache-dir -r requirements.txt\r\nEXPOSE 8080\r\nCMD .\/micro-svc-demo.py\r\nThe service can then be run passing the parameters through the environment:\r\ndocker run -d --name microservice \\\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 -p 8080:8080 \\\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 -e COUCHBASE_HOST=$COUCHBASE_HOST \\\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 -e COUCHBASE_USER=$COUCHBASE_USER \\\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 -e COUCHBASE_PASSWORD=$COUCHBASE_PASS \\\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 -e COUCHBASE_BUCKET=$COUCHBASE_BUCKET \\\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 testsvc<\/pre>\n<h2>FastAPI<\/h2>\n<p>Some time ago, WSGI (Web Server Gateway Interface) was created for Python web frameworks. It enabled a developer to only focus on building web applications instead of all the other lower-level tasks required with a web server. That standard was extended into ASGI (Asynchronous Server Gateway Interface), which supports asynchronous Python programming and therefore is well suited for stateless applications such as REST APIs.<\/p>\n<p><a href=\"https:\/\/www.uvicorn.org\/\" target=\"_blank\" rel=\"noopener\">Uvicorn<\/a> is an ASGI web server implementation for Python, and FastAPI integrates with Uvicron to create a rapid API development platform. I decided to use this to create a second API implementation to compare to the entire code version. Since it fully supports asynchronous Python, it also plays well with the Couchbase Python SDK, which fully supports asynchronous programming.<\/p>\n<p>Using this framework accelerates development because much less code is needed than the full code version. Some functions to connect to Couchbase are required, but beyond that, decorated app methods are used to interact with the FastAPI instance calling minimal code segments to fetch and return data. As with the full code version, the service connects to Couchbase once and uses the resulting collection methods to get data. The <em>on_event<\/em> method is used on startup to connect to Couchbase, retrieve the auth token, and set all the needed variables.<\/p>\n<pre class=\"decode-attributes:false lang:python decode:true\">@app.on_event(\"startup\")\r\nasync def service_init():\r\n\u00a0\u00a0\u00a0 key_id = '1'\r\n\u00a0\u00a0\u00a0 cluster[1] = await get_cluster()\r\n\u00a0\u00a0\u00a0 collections['service_auth'] = await get_collection(cluster[1], 'service_auth')\r\n\u00a0\u00a0\u00a0 doc_id = f\"service_auth:{key_id}\"\r\n\u00a0\u00a0\u00a0 result = await collections['service_auth'].lookup_in(doc_id, [SD.get('token')])\r\n\u00a0\u00a0\u00a0 auth_token[1] = result.content_as[str](0)\r\n\u00a0\u00a0\u00a0 collections['user_data'] = await get_collection(cluster[1], 'user_data')\r\n\u00a0\u00a0\u00a0 collections['user_images'] = await get_collection(cluster[1], 'user_images')<\/pre>\n<p>Once the startup actions are complete, short functions for each possible request path are invoked through app method calls. The path parameter is extracted from the path and passed to the function, along with a dependency on the function to check the auth token. With this implementation, only environment variables are used to pass connection parameters.<\/p>\n<pre class=\"decode-attributes:false lang:python decode:true\">@app.get(\"\/api\/v1\/id\/{document}\", response_model=Profile)\r\nasync def get_by_id(document: str, authorized: bool = Depends(verify_token)):\r\n\u00a0\u00a0\u00a0 if authorized:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 profile = await get_profile(collection=collections['user_data'], collection_name='user_data', document=document)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return profile<\/pre>\n<p>The container for this implementation can use the same base as the full code version and install the same dependencies; however, it will have a few extra Python package requirements and the service is invoked through Uvicorn.<\/p>\n<pre class=\"decode-attributes:false lang:python decode:true\">FROM python:3.9-bullseye\r\nRUN apt update\r\nRUN apt install elpa-magit -y\r\nRUN apt install git-all python3-dev python3-pip python3-setuptools cmake build-essential libssl-dev -y\r\nWORKDIR \/usr\/src\/app\r\nADD . \/usr\/src\/app\r\nRUN pip install --no-cache-dir -r requirements.txt\r\nEXPOSE 8080\r\nCMD uvicorn service:app --host 0.0.0.0 --port 8080<\/pre>\n<h2>Setting up Node.js to test endpoints<\/h2>\n<p>The blog post is about Python, but it would be helpful to have a non-Python comparison for the API so for this I decided to use Node.js; it is asynchronous and works well with APIs. The Node.js implementation uses the Express module to create a Web Server, and in a similar fashion to FastAPI it uses the <em>app.get<\/em> method for all supported paths. It calls a function to check the auth token first, and if successful it calls a function to get the requested data.<\/p>\n<pre class=\"decode-attributes:false lang:python decode:true\">app.get('\/api\/v1\/nickname\/:nickname', checkToken, getRESTAPINickname);\r\napp.get('\/api\/v1\/username\/:username', checkToken, getRESTAPIUsername);\r\napp.get('\/api\/v1\/id\/:id', checkToken, getRESTAPIId);\r\napp.get('\/api\/v1\/picture\/record\/:id', checkToken, getRESTAPIPictureId);\r\napp.get('\/api\/v1\/picture\/raw\/:id', checkToken, getRESTAPIImageData);\r\napp.get('\/healthz', getHealthCheckPage);<\/pre>\n<p>There is a module for the Couchbase functions located in a JavaScript file, and the functions for the supported API calls are also in modules in separate JavaScript files. Like with Python, there is a Node container that is used as a base and the NPM utility maintains the dependencies and starts the service.<\/p>\n<pre class=\"decode-attributes:false lang:sh decode:true\">FROM node:16.14.2\r\nWORKDIR \/app\r\nADD . \/app\r\nRUN rm -rf \/app\/node_modules\r\nRUN npm install -g npm@latest\r\nRUN npm install\r\nEXPOSE 8080\r\nCMD npm start<\/pre>\n<h2>Kubernetes to spinup Couchbase autonomously<\/h2>\n<p>As mentioned earlier, Kubernetes was chosen to test the service implementations. This enabled accelerated testing due to the ability to rapidly deploy and scale the services for different test scenarios. There are two options for using Couchbase with Kubernetes. The Couchbase Autonomous Operator can be used to deploy Couchbase into the Kubernetes environment, or the service can connect to an external cluster. The service was tested with an external cluster that was deployed in the same cloud VPC. All nodes were in the same cloud region, and both the Couchbase cluster nodes and Kubernetes nodes were deployed across availability zones to simulate what would likely be seen in a real-world deployment.<\/p>\n<p><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image001.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-13035\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image001.png\" alt=\"Using Kubernetes to test Couchbase implementations\" width=\"953\" height=\"753\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image001.png 953w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image001-300x237.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image001-768x607.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image001-20x16.png 20w\" sizes=\"auto, (max-width: 953px) 100vw, 953px\" \/><\/a><\/p>\n<p>Three deployment YAML files were created to deploy the three implementations. Each deployment YAML creates a namespace for the service. It uses a secret for the Couchbase password. The service is deployed with 4 replicas initially. As it is a stateless microservice, it can scale up and down as needed. Traffic is directed to the service with a load balancer. As the Kubernetes environment used was integrated with a cloud provider, each deployment also provisioned a cloud load balancer for the service.<\/p>\n<pre class=\"decode-attributes:false lang:js decode:true\">apiVersion: v1\r\nkind: Namespace\r\nmetadata:\r\n\u00a0 name: demopy\r\n---\r\napiVersion: v1\r\nkind: Secret\r\nmetadata:\r\n\u00a0 name: demopy-secrets\r\n\u00a0 namespace: demopy\r\ntype: Opaque\r\ndata:\r\n\u00a0 adminPassword: aBcDeFgH=\r\n---\r\napiVersion: apps\/v1\r\nkind: Deployment\r\nmetadata:\r\n\u00a0 name: demopy\r\n\u00a0 namespace: demopy\r\nspec:\r\n\u00a0 replicas: 4\r\n\u00a0 selector:\r\n\u00a0\u00a0\u00a0 matchLabels:\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0app: demopy\r\n\u00a0 strategy:\r\n\u00a0\u00a0\u00a0 type: RollingUpdate\r\n\u00a0\u00a0\u00a0 rollingUpdate:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 maxUnavailable: 25%\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 maxSurge: 1\r\n\u00a0 template:\r\n\u00a0\u00a0\u00a0 metadata:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 labels:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 app: demopy\r\n\u00a0\u00a0\u00a0 spec:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 containers:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 - name: demopy\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 image: mminichino\/demopy:1.0.5\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 imagePullPolicy: Always\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ports:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 - name: app-port\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 containerPort: 8080\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 env:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 - name: COUCHBASE_HOST\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 value: 1.2.3.4\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 - name: COUCHBASE_USER\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 value: Administrator\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 - name: COUCHBASE_PASSWORD\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 valueFrom:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 secretKeyRef:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 name: demopy-secrets\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 key: adminPassword\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 - name: COUCHBASE_BUCKET\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 value: sample_app\r\n---\r\napiVersion: v1\r\nkind: Service\r\nmetadata:\r\n\u00a0 name: demopy-service\r\n\u00a0 namespace: demopy\r\n\u00a0 labels:\r\n\u00a0\u00a0\u00a0 app: demopy\r\nspec:\r\n\u00a0 selector:\r\n\u00a0\u00a0\u00a0 app: demopy\r\n\u00a0 ports:\r\n\u00a0 - name: http\r\n\u00a0\u00a0\u00a0 port: 8080\r\n\u00a0\u00a0\u00a0 targetPort: 8080\r\n\u00a0 type: LoadBalancer<\/pre>\n<p>Using the deployment YAML files, the service can be deployed and scaled as needed with the Kubernetes CLI. Optionally if this was an actual production environment, tools such as autoscaling and advanced load balancing could be used to control and access the deployment.<\/p>\n<pre class=\"decode-attributes:false lang:sh decode:true\">$ kubectl apply -f demopy.yaml\r\n$ kubectl scale deployment --replicas=8 demopy -n demopy<\/pre>\n<h2>Cluster performance results<\/h2>\n<p>Before testing the services, the Couchbase cluster was tested from the Kubernetes cluster to create a baseline. YCSB workload B was used (which is primarily key-value <em>get<\/em> operations) and it yielded 156,094 ops\/s. API testing was done with Apache JMeter. The ID API call was used to keep it simple, and the JMeter random number generator was leveraged to create test runs against random user profiles. The test scenario was time bound with a run time of three minutes where it would generate unrestrained load against the load balancer service requesting random user profiles with no ramp-up (the load was constant for the whole test duration).<\/p>\n<p><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image003.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13037 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image003-1024x646.png\" alt=\"Setting REST service thread groups with JMeter\" width=\"900\" height=\"568\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image003-1024x646.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image003-300x189.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image003-768x485.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image003-20x13.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image003-1320x833.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image003.png 1431w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image005.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-13038\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image005-1024x607.png\" alt=\"Testing REST service endpointswith JMeter\" width=\"900\" height=\"533\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image005-1024x607.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image005-300x178.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image005-768x455.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image005-20x12.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image005-1320x782.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image005.png 1431w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/a><\/p>\n<p>For the first set of tests, the JMeter test parameters were unchanged, and what varied was the scale of the three API implementations. Testing started with 4 Pods for each implementation deployment and scaled up to 8 and finally 16 Pods. All the implementations scaled throughput as the Pods scaled in the deployment.<\/p>\n<p><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image008.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-13039\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image008.jpg\" alt=\"Couchbase Python performance results - requests\/sec\" width=\"469\" height=\"300\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image008.jpg 469w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image008-300x192.jpg 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image008-20x13.jpg 20w\" sizes=\"auto, (max-width: 469px) 100vw, 469px\" \/><\/a><\/p>\n<p>Node.js fared the best with this test strategy as it had the lowest average latency. One millisecond is not a lot of latency, nor is 12 milliseconds. But with a fixed number of generator threads creating over 1 million requests in three minutes, milliseconds have a cumulative effect. However, please bear in mind that this is an extreme test. These are just data points. What was surprising was that the full code Python service kept pace with the FastAPI implementation.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13040 size-full\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image010.jpg\" alt=\"Couchbase Python performance results - latency\" width=\"469\" height=\"310\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image010.jpg 469w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image010-300x198.jpg 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image010-20x13.jpg 20w\" sizes=\"auto, (max-width: 469px) 100vw, 469px\" \/><\/p>\n<p>Since the first test scenario demonstrated that both the full code Python and FastAPI implementations were scalable, the second round of tests scaled the number of request threads with a fixed number of 32 service Pods. With this test scenario, the Python-based services could scale near 10,000 requests per second.<a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image012.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13041 size-full\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/04\/image012.jpg\" alt=\"Couchbase Python performance results - request thread scale\" width=\"469\" height=\"334\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image012.jpg 469w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image012-300x214.jpg 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/image012-20x14.jpg 20w\" sizes=\"auto, (max-width: 469px) 100vw, 469px\" \/><\/a><\/p>\n<h2>Conclusions<\/h2>\n<p>I think that Python is an excellent option for moderate load services. With all the testing done, the Kubernetes cluster nodes had ample available CPU and memory, so there was plenty of headroom to scale the service as needed. For implementations that require massive scale at the lowest latency, then Node.js may be a better option. Couchbase supports all the prevalent languages, so just as I was able to easily code three microservice implementations, anyone can use multiple languages and frameworks and integrate Couchbase with ease.<\/p>\n<h3>Up Next<\/h3>\n<p>In the next post in this blog series, I will talk about generating random test data for the microservice schema. Here are links to the resources mentioned in this post:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-1\/\" target=\"_blank\" rel=\"noopener\">Build A Python Microservice With Couchbase &#8211; Part 1<\/a><\/li>\n<li>Part 2 &#8211; Microservice source code for user profile\n<ul>\n<li><a href=\"https:\/\/github.com\/mminichino\/user-profile-demo\">https:\/\/github.com\/mminichino\/user-profile-demo<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/mminichino\/user-profile-demo-fastapi\">https:\/\/github.com\/mminichino\/user-profile-demo-fastapi<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/mminichino\/node-user-profile-demo\">https:\/\/github.com\/mminichino\/node-user-profile-demo<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/cloud.couchbase.com\/\">Capella Couchbase Cloud offering<\/a><\/li>\n<li><a href=\"https:\/\/www.couchbase.com\/products\/cloud\/kubernetes\/\">Couchbase Autonomous Operator<\/a><\/li>\n<\/ul>\n<h3>Random fun fact<\/h3>\n<p>HTTP response codes are defined per the protocol specification. The 400 range is reserved for situations where the error seems to have been caused by the client. HTTP 418 is the &#8220;I&#8217;m a teapot&#8221; error and the spec states the &#8220;I&#8217;m a teapot client error response code indicates that the server refuses to brew coffee because it is, permanently, a teapot. A combined coffee\/teapot that is temporarily out of coffee should instead return 503.&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the first installment of this series, we discussed the drivers behind creating microservices, and why Couchbase is the perfect datastore to use with a microservice architecture. With their stateless nature, they can be deployed anywhere and horizontally scaled as [&hellip;]<\/p>\n","protected":false},"author":81015,"featured_media":13042,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1816,9139,2201],"tags":[2103,9548],"ppma_author":[9550],"class_list":["post-13033","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-couchbase-server","category-python","category-tools-sdks","tag-microservices","tag-service-oriented-architecture"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.9 (Yoast SEO v25.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Build A Python Microservice With Couchbase \u2013 Part 2<\/title>\n<meta name=\"description\" content=\"Learn to build fully-scalable microservices using Python and Couchbase Autonomous Operator? Test the API with JMeter and Node.js.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Build A Python Microservice With Couchbase \u2013 Part 2\" \/>\n<meta property=\"og:description\" content=\"Learn to build fully-scalable microservices using Python and Couchbase Autonomous Operator? Test the API with JMeter and Node.js.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-04-11T15:56:35+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T04:25:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1709\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Michael Minichino\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Michael Minichino\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/\"},\"author\":{\"name\":\"Michael Minichino\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/15ea6a51d53d4739913c98d25a8d7e77\"},\"headline\":\"Build A Python Microservice With Couchbase \u2013 Part 2\",\"datePublished\":\"2022-04-11T15:56:35+00:00\",\"dateModified\":\"2025-06-14T04:25:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/\"},\"wordCount\":2218,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg\",\"keywords\":[\"microservices\",\"service-oriented architecture\"],\"articleSection\":[\"Couchbase Server\",\"Python\",\"Tools &amp; SDKs\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/\",\"name\":\"Build A Python Microservice With Couchbase \u2013 Part 2\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg\",\"datePublished\":\"2022-04-11T15:56:35+00:00\",\"dateModified\":\"2025-06-14T04:25:00+00:00\",\"description\":\"Learn to build fully-scalable microservices using Python and Couchbase Autonomous Operator? Test the API with JMeter and Node.js.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg\",\"width\":2560,\"height\":1709,\"caption\":\"Building a microservice API with Couchbase and Python\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Build A Python Microservice With Couchbase \u2013 Part 2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/15ea6a51d53d4739913c98d25a8d7e77\",\"name\":\"Michael Minichino\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/ed87fc8ff8aedc56f9872fbd77382f29\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/03\/Screen-Shot-2022-03-28-at-12.40.06-PM.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/03\/Screen-Shot-2022-03-28-at-12.40.06-PM.png\",\"caption\":\"Michael Minichino\"},\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/michael-minichino\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Build A Python Microservice With Couchbase \u2013 Part 2","description":"Learn to build fully-scalable microservices using Python and Couchbase Autonomous Operator? Test the API with JMeter and Node.js.","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:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/","og_locale":"en_US","og_type":"article","og_title":"Build A Python Microservice With Couchbase \u2013 Part 2","og_description":"Learn to build fully-scalable microservices using Python and Couchbase Autonomous Operator? Test the API with JMeter and Node.js.","og_url":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/","og_site_name":"The Couchbase Blog","article_published_time":"2022-04-11T15:56:35+00:00","article_modified_time":"2025-06-14T04:25:00+00:00","og_image":[{"width":2560,"height":1709,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg","type":"image\/jpeg"}],"author":"Michael Minichino","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Michael Minichino","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/"},"author":{"name":"Michael Minichino","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/15ea6a51d53d4739913c98d25a8d7e77"},"headline":"Build A Python Microservice With Couchbase \u2013 Part 2","datePublished":"2022-04-11T15:56:35+00:00","dateModified":"2025-06-14T04:25:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/"},"wordCount":2218,"commentCount":1,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg","keywords":["microservices","service-oriented architecture"],"articleSection":["Couchbase Server","Python","Tools &amp; SDKs"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/","url":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/","name":"Build A Python Microservice With Couchbase \u2013 Part 2","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg","datePublished":"2022-04-11T15:56:35+00:00","dateModified":"2025-06-14T04:25:00+00:00","description":"Learn to build fully-scalable microservices using Python and Couchbase Autonomous Operator? Test the API with JMeter and Node.js.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/04\/build-a-microservice-api-with-python-and-couchbase-scaled.jpeg","width":2560,"height":1709,"caption":"Building a microservice API with Couchbase and Python"},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/build-a-python-microservice-with-couchbase-part-2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Build A Python Microservice With Couchbase \u2013 Part 2"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/15ea6a51d53d4739913c98d25a8d7e77","name":"Michael Minichino","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/ed87fc8ff8aedc56f9872fbd77382f29","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/03\/Screen-Shot-2022-03-28-at-12.40.06-PM.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/03\/Screen-Shot-2022-03-28-at-12.40.06-PM.png","caption":"Michael Minichino"},"url":"https:\/\/www.couchbase.com\/blog\/author\/michael-minichino\/"}]}},"authors":[{"term_id":9550,"user_id":81015,"is_guest":0,"slug":"michael-minichino","display_name":"Michael Minichino","avatar_url":{"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/03\/Screen-Shot-2022-03-28-at-12.40.06-PM.png","url2x":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/03\/Screen-Shot-2022-03-28-at-12.40.06-PM.png"},"author_category":"","last_name":"Minichino","first_name":"Michael","job_title":"","user_url":"","description":"Michael Minichino is a Principal Solutions Engineer at Couchbase"}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/13033","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/81015"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=13033"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/13033\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/13042"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=13033"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=13033"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=13033"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=13033"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}