<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stephen A. Lizcano</title>
    <description>The latest articles on DEV Community by Stephen A. Lizcano (@stevelizcano).</description>
    <link>https://dev.to/stevelizcano</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F101474%2F3803e715-e278-4caf-89c8-8c32da93dcd0.jpg</url>
      <title>DEV Community: Stephen A. Lizcano</title>
      <link>https://dev.to/stevelizcano</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stevelizcano"/>
    <language>en</language>
    <item>
      <title>How Kubernetes Works</title>
      <dc:creator>Stephen A. Lizcano</dc:creator>
      <pubDate>Fri, 18 Sep 2020 09:49:06 +0000</pubDate>
      <link>https://dev.to/stevelizcano/how-kubernetes-works-396f</link>
      <guid>https://dev.to/stevelizcano/how-kubernetes-works-396f</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ol2zcn0rcoojvutidjb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ol2zcn0rcoojvutidjb.jpg" alt="Ship wheel for kubernetes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Containers are useful for many use cases, from tests, developmnent, and production apps.&lt;/p&gt;

&lt;p&gt;The arrival of containers the software development completely. They enabled a way to create repeatable, consistent environments to run code.&lt;/p&gt;

&lt;p&gt;Never again could someone say "Well, it worked on my machine." Instead of managing your own OS files, updates, and hardware, the container system allows us to focus instead on our code itself. It takes the security and resource features of VMs without the costs of maintaining your own OS or hardware. &lt;/p&gt;

&lt;p&gt;And thus a new era on how teams build and ship software was born. &lt;/p&gt;

&lt;p&gt;With developers seeing the benefits of containers in the development and production lifecycle, the need for a contianer orchestration became apparent.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;Kubernetes&lt;/strong&gt; (or &lt;em&gt;k8s&lt;/em&gt; as it is abbreviated), which is currently the most popular and in-demand container orchestration tool.&lt;/p&gt;

&lt;p&gt;In this post we'll go over Kubernetes and its concepts, how it works and it is structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Starting only in 2014, Kubernetes has risen to the #1 container orchestration platform, over Docker Swarm and Apache Mesos. It enjoys high demand due to its quick release cycle  most popular tool to manage container infrastructure — with enterprise customers especially.&lt;/p&gt;

&lt;p&gt;Kubernetes takes this evolution of container orchestration a step further. It gives a consistent, (relatively) cloud neutral way of deploying and orchestrating your container environments in an Infrastructure as Code (IAC) style.&lt;/p&gt;

&lt;p&gt;With it one can define an entire application's structure using YAML. This includes everything from Load Balancers, autoscaling, TLS, networking rules, and more.&lt;/p&gt;

&lt;p&gt;Combining it with a CI/CD workflow and you have a powerful method of defining, deploying and updating your architecture with speed and high availability. This ability has enabled the fast adoption of Kubernetes worldwide, with &lt;strong&gt;countless organizations adopting it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's complex however, and it's important to understand how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes Fundamental Concepts
&lt;/h2&gt;

&lt;p&gt;The goal of Kubernetes is to coordinate your containers to be highly available and to &lt;em&gt;act as a single unit&lt;/em&gt;. All applications need to be containerized for it to work. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;em&gt;If you don't know containers well I highly recommend &lt;a href="https://btholt.github.io/complete-intro-to-containers/intro" rel="noopener noreferrer"&gt;Brian Holt's Complete Intro to Containers&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Basic Architecture
&lt;/h3&gt;

&lt;p&gt;Kubernetes has a "hub and spoke" pattern, where control of a &lt;em&gt;cluster&lt;/em&gt;  of containers is orchestrated from an API server that fans out to &lt;em&gt;nodes&lt;/em&gt; who then talk to &lt;em&gt;pods&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxts8aezxjuphwbjblpa2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxts8aezxjuphwbjblpa2.png" title="Diagram of a node and its pods." alt="Node diagram"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Diagram of a node and its pods&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The master (or control plane)
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;master&lt;/strong&gt;, or control plane, acts as the "brain" of kubernetes. Monitors the entire cluster, and manages its operation using the Kubernetes API. &lt;/p&gt;

&lt;p&gt;The master handles the High Availability aspects of Kubernetes, from self healing of nodes and pods to replacement if a node goes down.&lt;/p&gt;

&lt;h3&gt;
  
  
  The node
&lt;/h3&gt;

&lt;p&gt;Nodes are individual worker servers (computers or VMs) that host groups of containers, depending on your design. They are responsible for checking the health of each container on the node, and restarting or repairing containers that have problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pod
&lt;/h3&gt;

&lt;p&gt;The smallest unit item in Kubernetes is the Pod. Usually a single container run from an image (the kind you build with a Dockerfile) pulled from some container registry. &lt;/p&gt;

&lt;p&gt;A Pod can run multiple containers, but that's an advanced use case like &lt;a href="https://www.magalix.com/blog/the-sidecar-pattern" rel="noopener noreferrer"&gt;sidecars&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Services and Deployments&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;But how do we manage application lifecyle, and scaling, or networking? Kubernetes separates them into &lt;em&gt;services&lt;/em&gt; and &lt;em&gt;deployments&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Deployments&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;deployment&lt;/strong&gt; definition handles your full applicaton lifecycle, like rollouts of new pods, to auto healing and auto scaling.&lt;/p&gt;

&lt;p&gt;A deployment spec will take in your desired Pod state (such as images) along with information about a desired number of containers, and how a rollout of updates should work.&lt;/p&gt;

&lt;p&gt;Deployment controllers monitor your images for changes, and will use the ReplicaSet specified within your deployment yaml specification to deploy changes, handle failures and rollbacks, all automatically for you.&lt;/p&gt;

&lt;p&gt;They are a high level Controller that combine other services like ReplicaSets and others to manage your app's lifecycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Networking with Services&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Kubernetes doesn't automatically setup any networking for us. It expects us to define them in our .yaml files. To do this, we use &lt;em&gt;services&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvudnm40hcehg878jqy7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvudnm40hcehg878jqy7k.png" alt="Service map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Kubernetes &lt;strong&gt;service&lt;/strong&gt; is a policy definition for handling networking of pods in your cluster, for inside and outside communication.&lt;/p&gt;

&lt;p&gt;Since pods can be created or destroyed at any time, it's important to define how pods should be able to contact one another regardless of Pod lifecycle. A Service definition defines your networking topology within your cluster.&lt;/p&gt;

&lt;p&gt;Example: A frontend pod (serving a static web page for example) needs to communicate with a backend pod (maybe a DB or redis cache). If you deploy a new update to the backend pod, how does the frontend pod know the new IP of the backend pod?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4n3cxvvgqh3464rsylh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4n3cxvvgqh3464rsylh6.png" alt="Node port"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the most common types of Services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ClusterIP:&lt;/strong&gt; The default service. It exposes a service IP available &lt;em&gt;for the cluster only.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NodePort:&lt;/strong&gt; Exposes a port on the external cluster IP (called &lt;code&gt;NodePort&lt;/code&gt;), and creates a port for the node itself (called &lt;code&gt;port&lt;/code&gt;)s , that allows you to connect to other ports or externally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoadBalancer:&lt;/strong&gt; For using external cloud load balancers for cloud deployments, such as the ALB from AWS or Cloud Load Balancer from GCP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ingress:&lt;/strong&gt; A method for using a third party load balancer (such as nginx, HAProxy, or Envoy) within your cluster. Currently becoming the defacto method for implementing load balancers in clusters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Controllers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Controllers &lt;em&gt;track a resource(s)&lt;/em&gt; and makes sure they are in the &lt;em&gt;desired state&lt;/em&gt;. This could be something like auto scaling on high loads, keeping a minimum number of pods running, to even running like cron jobs.&lt;/p&gt;

&lt;p&gt;The most important ones for beginners are:&lt;/p&gt;

&lt;h4&gt;
  
  
  StatefulSets
&lt;/h4&gt;

&lt;p&gt;StatefulSets match containers to storage volumes, providing persistence to your workloads (like running PostgreSQL, for example).&lt;/p&gt;

&lt;h4&gt;
  
  
  ReplicaSets
&lt;/h4&gt;

&lt;p&gt;ReplicaSets ensure that you have a minimum number of pods you specify be deployed at all times.&lt;/p&gt;

&lt;p&gt;You would typically want to use Deployments instead of replicasets, as they manage ReplicaSets and have other useful features.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;And that's pretty much it! This overview gives you a good understanding of how kubernetes works conceptually, and will allow you to understand what is going on in the background and how it connects everything together.&lt;/p&gt;

&lt;p&gt;Need help with kubernetes? You can contact me &lt;a href="mailto:stephen@lizcano.dev"&gt;stephen@lizcano.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>containers</category>
      <category>docker</category>
      <category>cloud</category>
    </item>
    <item>
      <title>DynamoDB Crash Course — Intro and Secondary Indexes</title>
      <dc:creator>Stephen A. Lizcano</dc:creator>
      <pubDate>Wed, 11 Mar 2020 16:33:15 +0000</pubDate>
      <link>https://dev.to/stevelizcano/dynamodb-crash-course-concepts-modeling-your-schema-and-secondary-indexes-2pkg</link>
      <guid>https://dev.to/stevelizcano/dynamodb-crash-course-concepts-modeling-your-schema-and-secondary-indexes-2pkg</guid>
      <description>&lt;p&gt;DynamoDB is increasingly gaining the spotlight as a performant, low-cost and serverless NoSQL Database. &lt;/p&gt;

&lt;p&gt;For good reason! It's a &lt;em&gt;fast, highly available, and serverless&lt;/em&gt; key-value &amp;amp; document database that is fully managed. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meaning:&lt;/strong&gt; Once you understand DynamoDB concepts, developers enjoy increased development speed  by letting DynamoDB handle the things that typically time — database scaling, availability, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this post we're going cover the basics of DynamoDB&lt;/strong&gt;: how it models data &amp;amp; access patterns, tips on when it's best to be used vs. using something like SQL, and how to extend access patterns across more of your data using something called &lt;strong&gt;Secondary Indexes&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What's NoSQL?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;NoSQL means simply: a database that doesn't typically follow the SQL style of a tabular, relational database, and the SQL language of accessing and querying data that data.&lt;/p&gt;

&lt;p&gt;In comparison, &lt;strong&gt;Relational Databases&lt;/strong&gt; (MySQL, PostgreSQL, etc.) do most of the the query processing for you—however you choose to define it— and &lt;em&gt;right at the data source&lt;/em&gt; (e.g. calculating total count of users, filtering of data, etc.).&lt;/p&gt;

&lt;p&gt;For NoSQL, it goes in reverse: a “query first” approach to identify the queries your application needs &lt;em&gt;before&lt;/em&gt; you design the database schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this means for the developer:&lt;/strong&gt; you need to understand you data access patterns &lt;em&gt;beforehand&lt;/em&gt; to fully utilize DynamoDB effectively, or you will run into issues later.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tip: If you are unsure how your access patterns are going to be and that they might change in the future, it might be better to use a SQL type of database.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What is DynamoDB?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftmrw4nstd64gzfq7jkv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftmrw4nstd64gzfq7jkv8.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DynamoDB is a popular &lt;strong&gt;NoSQL&lt;/strong&gt; database provided by Amazon Web Services that acts as a &lt;strong&gt;key-value &amp;amp; document store&lt;/strong&gt; that provides data with &lt;strong&gt;millisecond performance&lt;/strong&gt; at scale. &lt;/p&gt;

&lt;p&gt;Basically, it's &lt;em&gt;super fast&lt;/em&gt;, &lt;em&gt;serverless, almost fully managed, and works at any scale.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Some of the primary benefits of DynamoDB are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance  — S*ingle digit* millisecond response times at any scale&lt;/li&gt;
&lt;li&gt;Serverless — With no servers to manage, DynamoDB takes all the headache away of managing and maintaining database servers.&lt;/li&gt;
&lt;li&gt;Consistency — ACID transactions for industries like business &amp;amp; finance.  If you put data into it, it's immediately available, and you can assure it'll never get lost (barring disasters, of course).&lt;/li&gt;
&lt;li&gt;Widely adopted: It used by many leading tech companies today, from AirBnB to Samsung, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DynamoDB Pricing &amp;amp; Capacity
&lt;/h3&gt;

&lt;p&gt;DynamoDB pricing exists in two modes: &lt;em&gt;provisioned&lt;/em&gt; and &lt;em&gt;on-demand.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned&lt;/strong&gt; is more cost effective if you have consistent workloads, with auto scaling capability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-Demand&lt;/strong&gt; Takes care of managing capacity for you, and you only pay for what you consume. &lt;em&gt;You should start with this, especially for personal projects.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DynamoDB Basics - Tables and Keys
&lt;/h3&gt;

&lt;p&gt;Data is organized in &lt;strong&gt;Tables,&lt;/strong&gt; that have a collection of &lt;strong&gt;items&lt;/strong&gt; set according to a &lt;strong&gt;schema&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table:&lt;/strong&gt;  a collection of items&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Item:&lt;/strong&gt; collection of &lt;strong&gt;attributes&lt;/strong&gt; (similar to a row or records in databases or spreadsheets). &lt;/p&gt;

&lt;p&gt;Let's imagine we have a table of users that is a summary of game scores. The table below has games they played, and some stats like Top Scores and date, and Wins and Losses. GameScores is the table, and each row below is an item:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fehhdml126n54yo64aw3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fehhdml126n54yo64aw3c.png" alt="DynamoDB Table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order for us to setup a table, we need to tell DynamoDB what our &lt;strong&gt;partition key&lt;/strong&gt; will be, and in effect, what our data access patterns will be.&lt;/p&gt;

&lt;p&gt;This is because DynamoDB &lt;strong&gt;stores and accesses data according to this value&lt;/strong&gt;. This is &lt;em&gt;key&lt;/em&gt; to understanding why it's important to understand your access patterns getting started—if they change, you might finding yourself having to create a new table and moving over your stored data!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Partition &amp;amp; Composite Keys&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When you make a table, each table must have a &lt;strong&gt;primary key&lt;/strong&gt; that uniquely identifies each item in a table (this is how DynamoDB knows where to find an item). A primary key can be one of two items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Partition Key:&lt;/strong&gt; A simple key that is one attribute (like &lt;em&gt;userID&lt;/em&gt;). (also called a &lt;em&gt;hash key,&lt;/em&gt; since it is used internally in Dynamo as output from the hashing function)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite Key:&lt;/strong&gt; In the GameScores table above, a user can have multiple forum posts. To make each item unique, we add a &lt;strong&gt;sort Key&lt;/strong&gt; together with the &lt;strong&gt;partition key&lt;/strong&gt; that will make each item unique in the table. This is called the composite key, where together with the sort key, all items are unique.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;**Example&lt;/em&gt;&lt;em&gt;: Pulling all scores with &lt;code&gt;userID&lt;/code&gt; 101, and then sorting by the &lt;code&gt;GameTitle&lt;/code&gt; to get all high scores for all games.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Accessing data in DynamoDB&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You can access data with two primary operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;query&lt;/strong&gt; operation returns items based on a particular key (&lt;code&gt;userID&lt;/code&gt;, etc.). This is the primary, recommended method. It's fast, affordable, and is how DynamoDB should generally be used.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;scan&lt;/strong&gt; looks through the &lt;strong&gt;entire table&lt;/strong&gt; for items based on set attributes or filter, and returns all the items that match the attributes.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tip:&lt;/strong&gt; This can get expensive and is &lt;strong&gt;generally not recommended&lt;/strong&gt;, as your charges will be higher, scans are slower, and scan the entire table.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Expanding your Access Patterns with Secondary Indexes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In the table above we can only query forum posts by a user and sort by a date, but that's it. What if we want to expand further?  For example, in the image above, what if we wanted to get each &lt;code&gt;userID&lt;/code&gt; sorted by &lt;code&gt;TopScoreDateTime&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;With Dynamo we can use something called &lt;strong&gt;Secondary Indexes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A secondary index allows applications to benefit from having one or more &lt;strong&gt;secondary&lt;/strong&gt; keys, to expand your access to your data with attributes that are different from your primary key.&lt;/p&gt;

&lt;p&gt;Secondary Indexes come in two flavors: local and global.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;local secondary index&lt;/strong&gt; (&lt;strong&gt;LSI&lt;/strong&gt; for short) allows you to have an &lt;em&gt;alternate&lt;/em&gt; sort key *&lt;em&gt;for your table, while having the *same primary key&lt;/em&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For our GameScores table, if we have an LSI to get &lt;code&gt;TopScoreDateTime&lt;/code&gt;, our LSI would have the &lt;code&gt;UserId&lt;/code&gt; be the same primary key, and the second sort key would be &lt;code&gt;TopScoreDateTime&lt;/code&gt;, which would allow us to a user by sorted Top Score datetimes.&lt;/li&gt;
&lt;li&gt;You can only have one LSI on a table, and only 10GB total table size.&lt;/li&gt;
&lt;li&gt;You can only add an LSI &lt;strong&gt;when you create a table!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What about applications that might need many different queries on a table?&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;global secondary index or GSI&lt;/strong&gt; allows you to have an infinite amount of partition and sort keys on your table, at the cost of data replication.  This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You pay for the extra replicated data.&lt;/li&gt;
&lt;li&gt;But you can have multiple access patterns on the same table, up to 20.&lt;/li&gt;
&lt;li&gt;GSIs are only &lt;em&gt;eventually consistent&lt;/em&gt;, meaning — you may not have access to the data on the GSI right after you write to a table. This is often done within a second, however.&lt;/li&gt;
&lt;li&gt;Example: In the table above, what if we wanted to query &lt;code&gt;TopScore&lt;/code&gt; by &lt;code&gt;GameTitle&lt;/code&gt;? We would create a new GSI called &lt;code&gt;GameTitleIndex&lt;/code&gt; with &lt;code&gt;GameTitle&lt;/code&gt; as the primary key and &lt;code&gt;TopScore&lt;/code&gt; as the sort key. When we query it, we can then see which &lt;code&gt;UserId&lt;/code&gt; got the top score across all games.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This post should help you understand what DynamoDB is, what makes it special, and understand how DynamoDB structures its data and how to query it.&lt;/p&gt;

&lt;p&gt;We didn't cover other topics like DynamoDB Accelerator for things like caching, or Streams for things like triggering lambda function after data entry. Those will be covered other posts, along with relational data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I hope this post was beneficial in understanding DynamoDB!&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;I help companies with cloud and mobile software, so if you have questions or need help, please feel free to reach out.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;p&gt;Stephen&lt;/p&gt;

</description>
      <category>aws</category>
      <category>dynamodb</category>
      <category>serverless</category>
      <category>webdev</category>
    </item>
    <item>
      <title>5 Minute Tutorial: Easily Get Base Types with AWS Amplify GraphQL Codegen for TypeScript</title>
      <dc:creator>Stephen A. Lizcano</dc:creator>
      <pubDate>Sun, 17 Nov 2019 18:23:21 +0000</pubDate>
      <link>https://dev.to/stevelizcano/5-minute-tutorial-get-base-types-from-your-aws-amplify-graphql-schema-with-typescript-3636</link>
      <guid>https://dev.to/stevelizcano/5-minute-tutorial-get-base-types-from-your-aws-amplify-graphql-schema-with-typescript-3636</guid>
      <description>&lt;p&gt;This short walkthrough illustrates how you can easily extend or generate your base Types for your TypeScript application easily using the AWS Ampligy/Apollo GraphQL codegen.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note, you should be slightly familiar with GraphQL and Apollo or AWS Amplify GraphQL services, and the basics of TypeScript&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why are base types important?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Base types, and specifically using TypeScript with the code generator allows us to make sure that the types we are creating, updating, or listing from our GraphQL api conform to our schema. &lt;/p&gt;

&lt;p&gt;This is especially important when designing our API function calls, so that everything we submit is of the correct shape and format, preventing us from dealing with errors later that could take a long time to debug.&lt;/p&gt;

&lt;p&gt;I work in Healthcare, specifically making web and mobile applications for physicians and administrators. &lt;/p&gt;

&lt;p&gt;So, we're going to use a simple healthcare focused schema to illustrate the example using medications and interactions.&lt;/p&gt;

&lt;p&gt;Schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# schema.graphql&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# @model automatically generates api calls for our API using GraphQL, AppSync &amp;amp; DynamoDB&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Patient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# basic patient information&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;sex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="n"&gt;encounters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Encounter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;Encounters&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Encounter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# when a patient sees a doctor, that's an Encounter&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;patient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Patient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;Encounters&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;EncounterOrders&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# For lab tests like xrays, MRI, etc&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# Tie the order to the encounter/patient visit it was created on&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Encounter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Encounter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;EncounterOrders&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is a basic one-to-many schema, that models a patient, an encounter with a physician, and sample clinical orders that can be made.&lt;/p&gt;

&lt;p&gt;To generate your API, use apollo or amplify GraphQL codegen using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;amplify codegen

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Follow the walkthrough, and be sure to select &lt;strong&gt;TypeScript&lt;/strong&gt; for your code generation language.&lt;/p&gt;

&lt;p&gt;You'll have a filed called &lt;strong&gt;API.ts&lt;/strong&gt; generated, most likely in your &lt;em&gt;./src&lt;/em&gt; folder if you're using React, React Native, etc.&lt;/p&gt;

&lt;p&gt;Open API.ts where you saved it, and look for the type GetPatientQuery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GetPatientQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;getPatient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Patient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;encounters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ModelEncounterConnection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Encounter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;nextToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;How do we extract the Patient base type? &lt;/p&gt;

&lt;p&gt;By utilizing TypeScripts &lt;a href="https://www.typescriptlang.org/docs/handbook/utility-types.html"&gt;Utility types&lt;/a&gt; to transform these types into the types all of our projects can use.&lt;/p&gt;

&lt;p&gt;We need to remove the possible nullish return of the API call, and any unnecessary annotations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Patient&lt;/span&gt;
  &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Exclude&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GetPatientQuery&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getPatient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;__typename&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's it! &lt;/p&gt;

&lt;p&gt;What's happening here is the &lt;a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#excludetu"&gt;Exclude utility type&lt;/a&gt; is removing/excluding the "null" of the type, which exists in case no data is returned/found.&lt;/p&gt;

&lt;p&gt;Next, the &lt;a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#excludetu"&gt;Omit utility type&lt;/a&gt; removes the "__typename" label that is generated by the codegen to tell us the base type name.&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;We can now use Patient as a base type for usage in our React/Vue/Angular etc web apps. We have the types for mapping, adding, and deleting types.&lt;/p&gt;

&lt;p&gt;We can extend this to all of the other types that we created in the Schema, too. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;And&lt;/em&gt; they automatically update if we change our schema!&lt;/p&gt;

&lt;p&gt;Cheers, and hope this was helpful!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Need help with Amplify, cloud, or full-stack? I'm available for consulting.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>graphql</category>
      <category>awsamplify</category>
      <category>awsappsync</category>
    </item>
  </channel>
</rss>
