<?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>Forem: oluseyeo</title>
    <description>The latest articles on Forem by oluseyeo (@oluseyeo).</description>
    <link>https://forem.com/oluseyeo</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%2F269383%2F4093de1f-bb0a-4ae6-b884-34d0723fc69b.png</url>
      <title>Forem: oluseyeo</title>
      <link>https://forem.com/oluseyeo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/oluseyeo"/>
    <language>en</language>
    <item>
      <title>Securing Kubernetes: Adding a new hostname or IP address to Kubernetes API Server</title>
      <dc:creator>oluseyeo</dc:creator>
      <pubDate>Thu, 30 Nov 2023 00:37:21 +0000</pubDate>
      <link>https://forem.com/oluseyeo/securing-kubernetes-adding-a-new-hostname-or-ip-address-to-kubernetes-api-server-5gpn</link>
      <guid>https://forem.com/oluseyeo/securing-kubernetes-adding-a-new-hostname-or-ip-address-to-kubernetes-api-server-5gpn</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# kubectl get ns&lt;/span&gt;
Unable to connect to the server: x509: certificate is valid ...
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Introduction:
&lt;/h1&gt;

&lt;p&gt;In the ever-evolving landscape of Kubernetes, securing connections to the API server is paramount. This article presents an approach on how to add a hostname or IP address to the TLS certificate used by the Kubernetes API server to allow continued secure access. Not assigning a static IP address to the control plane from get go, or using a new hostname or different hostname to access the API Server may result in the error highlighted above when attempting to access the cluster.&lt;/p&gt;

&lt;p&gt;A quick workaround is to use the &lt;code&gt;--insecure-skip-tls-verify&lt;/code&gt; command line argument with &lt;code&gt;kubectl&lt;/code&gt;. However, it's crucial to note that this method skips TLS verification, rendering the connection insecure, and thus improper for Production use.&lt;/p&gt;

&lt;p&gt;In a short delve into the technical background, it is important for know that the Kubernetes API server employs digital certificates to encrypt incoming and outgoing traffic and authenticate connections. For those interested in understanding the underlying processes, attempting to connect to the API server via &lt;code&gt;kubectl&lt;/code&gt; with a hostname or IP address not included in the certificate's Subject Alternative Names (SAN) will result in an error. This error signifies that the certificate is not valid for the specified IP address or hostname.&lt;/p&gt;

&lt;p&gt;To resolve this issue, it is necessary to update the certificate to include all relevant IP addresses or hostnames in the list of SANs. This ensures that the API server recognizes and validates connections initiated with the specified addresses or hostnames.&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%2Fuploads%2Farticles%2Fx2kjptd1xn5l7fxyxfro.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%2Fuploads%2Farticles%2Fx2kjptd1xn5l7fxyxfro.png" alt="Kubernetes Certificate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before You Begin:
&lt;/h2&gt;

&lt;p&gt;Before embarking on any updates, it's imperative to consider the production environment. For nodes actively serving in production, pull them out of the active node pool or gracefully drain live traffic using &lt;code&gt;kubectl drain&lt;/code&gt;. This ensures a smooth transition without disrupting critical workloads.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Updating Kubernetes API Server Certificates:
&lt;/h3&gt;

&lt;p&gt;Before proceeding with the certificate update, it is essential to ensure that the IP address of the node is accurately reflected across all pertinent manifest files. These Kubernetes component manifests are centrally located in the directory &lt;code&gt;/etc/kubernetes/manifests/&lt;/code&gt; and undergo real-time updates. Consequently, any modifications made to these files are promptly redeployed. The following steps outline the process:&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Update Kubelet config:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# vi /etc/default/kubelet&lt;/span&gt;
&lt;span class="nv"&gt;KUBELET_EXTRA_ARGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;--node-ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x.x.x.x &lt;span class="c"&gt;#newIPaddress&lt;/span&gt;
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fuploads%2Farticles%2F5cn10yoph45amx9upbgq.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%2Fuploads%2Farticles%2F5cn10yoph45amx9upbgq.png" alt="Terminal Showing kubelet config"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Update Kubernetes manifest
&lt;/h4&gt;

&lt;p&gt;Edit the following Kubernetes manifests files - &lt;code&gt;etcd.yaml&lt;/code&gt; and &lt;code&gt;kube-apiserver.yaml&lt;/code&gt;. In the &lt;code&gt;etcd.yaml&lt;/code&gt; - update the old IP address registered with the new IP address. You can execute the below, and it will update the address wherever it is present in the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# vi /etc/kubernetes/manifests/etcd.yaml&lt;/span&gt;
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the file is open, ensure you are escaped and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;:%s/&lt;span class="o"&gt;{&lt;/span&gt;oldIPaddress&lt;span class="o"&gt;}&lt;/span&gt;/&lt;span class="o"&gt;{&lt;/span&gt;newIPaddress&lt;span class="o"&gt;}&lt;/span&gt;/gc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;br&gt;
In the &lt;code&gt;kube-apiserver.yaml&lt;/code&gt; file, update the IP address value of the advertise-address and save the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# vi /etc/kubernetes/manifests/kube-apiserver.yaml&lt;/span&gt;
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;--&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kube-apiserver&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--advertise-address=x.x.x.x&lt;/span&gt; &lt;span class="c1"&gt;#newIPaddress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above steps executed, the manifest files are updated, and now you may proceed with updating the certificate accordingly.&lt;/p&gt;




&lt;h4&gt;
  
  
  Retrieve the kubeadm Configuration File:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/admin.conf
kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system get configmap kubeadm-config &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.ClusterConfiguration}'&lt;/span&gt; &lt;span class="nt"&gt;--insecure-skip-tls-verify&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; kubeadm.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  Fine-Tune the Configuration File:
&lt;/h4&gt;

&lt;p&gt;Open the saved &lt;code&gt;kubeadm.yaml&lt;/code&gt; in a text editor and navigate to the certSANs section under apiServer. Update the new IP address or hostname.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;certSANs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.10.10.100"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kubernetes.default"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new-hostname"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X.X.X.X"&lt;/span&gt; &lt;span class="c1"&gt;#newIPaddress&lt;/span&gt;
  &lt;span class="na"&gt;extraArgs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  Recreate API Server Certificates:
&lt;/h4&gt;

&lt;p&gt;This process will generate a new certificate and key for the API server, using the specified configuration file. Since the specified configuration file includes a certSANs list, &lt;code&gt;kubeadm&lt;/code&gt; will automatically add those SANs when creating the new certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; /etc/kubernetes/pki/apiserver.&lt;span class="o"&gt;{&lt;/span&gt;crt,key&lt;span class="o"&gt;}&lt;/span&gt; ~
kubeadm init phase certs apiserver &lt;span class="nt"&gt;--config&lt;/span&gt; kubeadm.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  Restart the kubeapiserver Container:
&lt;/h4&gt;

&lt;p&gt;Depending on the container runtime being used, follow either of the options below&lt;/p&gt;

&lt;h5&gt;
  
  
  Using Containerd as the runtime:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;crictl pods | grep kube-apiserver | cut -d' ' -f1&lt;/code&gt; to get the Pod ID for the Kubernetes API server Pod.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;crictl stop &amp;lt;pod-id&amp;gt;&lt;/code&gt; to stop the Pod.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;crictl rm &amp;lt;pod-id&amp;gt;&lt;/code&gt; to remove the Pod.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Using Docker as the runtime:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;docker ps | grep kube-apiserver | grep -v pause&lt;/code&gt; to get the container ID for the container running the Kubernetes API server.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;docker kill &amp;lt;containerID&amp;gt;&lt;/code&gt; to kill the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon completion of these steps, Kubelet will autonomously initiate the restart of the container, facilitating the adoption of the updated certificate. Following the API server's restart, immediate connectivity will be established, allowing access through either of the newly-added IP addresses or hostnames.&lt;/p&gt;




&lt;h3&gt;
  
  
  Validation:
&lt;/h3&gt;

&lt;p&gt;This is dual pronged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;kubectl&lt;/code&gt; to access the cluster and confirm that the node and pod are all accessible.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/admin.conf
kubectl get ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Alternatively, employ this verification method, leveraging OpenSSL on the Kubernetes control plane node. This step entails decoding the certificate and inspecting the list of Subject Alternative Names (SANs) embedded within the certificate:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect the "X509v3 Subject Alternative Name" line, which lists the DNS names and IP addresses incorporated as Subject Alternative Names (SANs) in the certificate. Executing this procedure should show the newly-added names and IP addresses specified in the modified &lt;code&gt;kubeadm&lt;/code&gt; configuration file. If any error persists, revisit the process, checking for common errors such as overlooking the removal of the previous certificate and key or neglecting to include &lt;code&gt;--config kubeadm.yaml&lt;/code&gt; in the &lt;code&gt;kubeadm init phase certs&lt;/code&gt; command.&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Updating the In-Cluster Configuration:
&lt;/h4&gt;

&lt;p&gt;Assuming a smooth execution of all changes, the final step entails updating the kubeadm ConfigMap stored in the cluster. This guarantees that when kubeadm is utilized for a cluster upgrade later, the updated information seamlessly integrates into the cluster&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubeadm config upload from-file &lt;span class="nt"&gt;--config&lt;/span&gt; kubeadm.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the successful application of changes to the configuration with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system get configmap kubeadm-config &lt;span class="nt"&gt;-o&lt;/span&gt; yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;In the dynamic world of Kubernetes, ensuring secure access to the API server is a critical aspect of maintaining a robust and reliable infrastructure. By carefully updating and fine-tuning the TLS certificates used by the Kubernetes API server, administrators can seamlessly integrate new IP addresses or hostnames into the certificate's Subject Alternative Names (SANs), thus enabling secure and uninterrupted access. However, it is crucial to follow the outlined steps diligently and verify the changes to guarantee a smooth transition without compromising the security of the Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and I hope someone finds this helpful. I am happy to chat further about new knowledge, opportunities and possible corrections. I can be reached on twitter via, @oluseyeo_&lt;/p&gt;

&lt;p&gt;Happy Hacking 💥 💥&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>security</category>
      <category>sitereliabilityengineering</category>
      <category>devops</category>
    </item>
    <item>
      <title>Securing Kubernetes: Adding a new hostname or IP address to Kubernetes API Server</title>
      <dc:creator>oluseyeo</dc:creator>
      <pubDate>Tue, 28 Nov 2023 05:19:19 +0000</pubDate>
      <link>https://forem.com/aws-builders/securing-kubernetes-api-server-adding-a-new-hostname-or-ip-address-to-kubernetes-api-server-1aej</link>
      <guid>https://forem.com/aws-builders/securing-kubernetes-api-server-adding-a-new-hostname-or-ip-address-to-kubernetes-api-server-1aej</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# kubectl get ns&lt;/span&gt;
Unable to connect to the server: x509: certificate is valid ...
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  Introduction:
&lt;/h1&gt;

&lt;p&gt;In the ever-evolving landscape of Kubernetes, securing connections to the API server is paramount. This article presents an approach on how to add a hostname or IP address to the TLS certificate used by the Kubernetes API server to allow continued secure access. Not assigning a static IP address to the control plane from get go, or using a new hostname or different hostname to access the API Server may result in the error highlighted above when attempting to access the cluster.&lt;/p&gt;

&lt;p&gt;A quick workaround is to use the &lt;code&gt;--insecure-skip-tls-verify&lt;/code&gt; command line argument with &lt;code&gt;kubectl&lt;/code&gt;. However, it's crucial to note that this method skips TLS verification, rendering the connection insecure, and thus improper for Production use.&lt;/p&gt;

&lt;p&gt;In a short delve into the technical background, it is important for know that the Kubernetes API server employs digital certificates to encrypt incoming and outgoing traffic and authenticate connections. For those interested in understanding the underlying processes, attempting to connect to the API server via &lt;code&gt;kubectl&lt;/code&gt; with a hostname or IP address not included in the certificate's Subject Alternative Names (SAN) will result in an error. This error signifies that the certificate is not valid for the specified IP address or hostname.&lt;/p&gt;

&lt;p&gt;To resolve this issue, it is necessary to update the certificate to include all relevant IP addresses or hostnames in the list of SANs. This ensures that the API server recognizes and validates connections initiated with the specified addresses or hostnames.&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%2Fuploads%2Farticles%2Fx2kjptd1xn5l7fxyxfro.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%2Fuploads%2Farticles%2Fx2kjptd1xn5l7fxyxfro.png" alt="Kubernetes Certificate"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Before You Begin:
&lt;/h2&gt;

&lt;p&gt;Before embarking on any updates, it's imperative to consider the production environment. For nodes actively serving in production, pull them out of the active node pool or gracefully drain live traffic using &lt;code&gt;kubectl drain&lt;/code&gt;. This ensures a smooth transition without disrupting critical workloads.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Updating Kubernetes API Server Certificates:
&lt;/h3&gt;

&lt;p&gt;Before proceeding with the certificate update, it is essential to ensure that the IP address of the node is accurately reflected across all pertinent manifest files. These Kubernetes component manifests are centrally located in the directory &lt;code&gt;/etc/kubernetes/manifests/&lt;/code&gt; and undergo real-time updates. Consequently, any modifications made to these files are promptly redeployed. The following steps outline the process:&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Update Kubelet config:
&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# vi /etc/default/kubelet&lt;/span&gt;
&lt;span class="nv"&gt;KUBELET_EXTRA_ARGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;--node-ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x.x.x.x &lt;span class="c"&gt;#newIPaddress&lt;/span&gt;
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;


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

&lt;/div&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%2Fuploads%2Farticles%2F5cn10yoph45amx9upbgq.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%2Fuploads%2Farticles%2F5cn10yoph45amx9upbgq.png" alt="Terminal Showing kubelet config"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Update Kubernetes manifest
&lt;/h4&gt;

&lt;p&gt;Edit the following Kubernetes manifests files - &lt;code&gt;etcd.yaml&lt;/code&gt; and &lt;code&gt;kube-apiserver.yaml&lt;/code&gt;. In the &lt;code&gt;etcd.yaml&lt;/code&gt; - update the old IP address registered with the new IP address. You can execute the below, and it will update the address wherever it is present in the file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# vi /etc/kubernetes/manifests/etcd.yaml&lt;/span&gt;
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Once the file is open, ensure you are escaped and run this command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

:%s/&lt;span class="o"&gt;{&lt;/span&gt;oldIPaddress&lt;span class="o"&gt;}&lt;/span&gt;/&lt;span class="o"&gt;{&lt;/span&gt;newIPaddress&lt;span class="o"&gt;}&lt;/span&gt;/gc


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

&lt;/div&gt;

&lt;p&gt; &lt;br&gt;
In the &lt;code&gt;kube-apiserver.yaml&lt;/code&gt; file, update the IP address value of the advertise-address and save the file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# vi /etc/kubernetes/manifests/kube-apiserver.yaml&lt;/span&gt;
&lt;span class="nv"&gt;$:&lt;/span&gt;&lt;span class="c"&gt;# &lt;/span&gt;


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

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="s"&gt;--&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kube-apiserver&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--advertise-address=x.x.x.x&lt;/span&gt; &lt;span class="c1"&gt;#newIPaddress&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;With the above steps executed, the manifest files are updated, and now you may proceed with updating the certificate accordingly.&lt;/p&gt;




&lt;h4&gt;
  
  
  Retrieve the kubeadm Configuration File:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/admin.conf
kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system get configmap kubeadm-config &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.ClusterConfiguration}'&lt;/span&gt; &lt;span class="nt"&gt;--insecure-skip-tls-verify&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; kubeadm.yaml


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

&lt;/div&gt;

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

&lt;h4&gt;
  
  
  Fine-Tune the Configuration File:
&lt;/h4&gt;

&lt;p&gt;Open the saved &lt;code&gt;kubeadm.yaml&lt;/code&gt; in a text editor and navigate to the certSANs section under apiServer. Update the new IP address or hostname.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;apiServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;certSANs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.10.10.100"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kubernetes.default"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new-hostname"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X.X.X.X"&lt;/span&gt; &lt;span class="c1"&gt;#newIPaddress&lt;/span&gt;
  &lt;span class="na"&gt;extraArgs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;h4&gt;
  
  
  Recreate API Server Certificates:
&lt;/h4&gt;

&lt;p&gt;This process will generate a new certificate and key for the API server, using the specified configuration file. Since the specified configuration file includes a certSANs list, &lt;code&gt;kubeadm&lt;/code&gt; will automatically add those SANs when creating the new certificate.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;mv&lt;/span&gt; /etc/kubernetes/pki/apiserver.&lt;span class="o"&gt;{&lt;/span&gt;crt,key&lt;span class="o"&gt;}&lt;/span&gt; ~
kubeadm init phase certs apiserver &lt;span class="nt"&gt;--config&lt;/span&gt; kubeadm.yaml


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

&lt;/div&gt;

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

&lt;h4&gt;
  
  
  Restart the kubeapiserver Container:
&lt;/h4&gt;

&lt;p&gt;Depending on the container runtime being used, follow either of the options below&lt;/p&gt;

&lt;h5&gt;
  
  
  Using Containerd as the runtime:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;crictl pods | grep kube-apiserver | cut -d' ' -f1&lt;/code&gt; to get the Pod ID for the Kubernetes API server Pod.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;crictl stop &amp;lt;pod-id&amp;gt;&lt;/code&gt; to stop the Pod.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;crictl rm &amp;lt;pod-id&amp;gt;&lt;/code&gt; to remove the Pod.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Using Docker as the runtime:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;docker ps | grep kube-apiserver | grep -v pause&lt;/code&gt; to get the container ID for the container running the Kubernetes API server.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;docker kill &amp;lt;containerID&amp;gt;&lt;/code&gt; to kill the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon completion of these steps, Kubelet will autonomously initiate the restart of the container, facilitating the adoption of the updated certificate. Following the API server's restart, immediate connectivity will be established, allowing access through either of the newly-added IP addresses or hostnames.&lt;/p&gt;




&lt;h3&gt;
  
  
  Validation:
&lt;/h3&gt;

&lt;p&gt;This is dual pronged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;kubectl&lt;/code&gt; to access the cluster and confirm that the node and pod are all accessible.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/admin.conf
kubectl get ns


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Alternatively, employ this verification method, leveraging OpenSSL on the Kubernetes control plane node. This step entails decoding the certificate and inspecting the list of Subject Alternative Names (SANs) embedded within the certificate:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text


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

&lt;/div&gt;

&lt;p&gt;Inspect the "X509v3 Subject Alternative Name" line, which lists the DNS names and IP addresses incorporated as Subject Alternative Names (SANs) in the certificate. Executing this procedure should show the newly-added names and IP addresses specified in the modified &lt;code&gt;kubeadm&lt;/code&gt; configuration file. If any error persists, revisit the process, checking for common errors such as overlooking the removal of the previous certificate and key or neglecting to include &lt;code&gt;--config kubeadm.yaml&lt;/code&gt; in the &lt;code&gt;kubeadm init phase certs&lt;/code&gt; command.&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Updating the In-Cluster Configuration:
&lt;/h4&gt;

&lt;p&gt;Assuming a smooth execution of all changes, the final step entails updating the kubeadm ConfigMap stored in the cluster. This guarantees that when kubeadm is utilized for a cluster upgrade later, the updated information seamlessly integrates into the cluster&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubeadm config upload from-file &lt;span class="nt"&gt;--config&lt;/span&gt; kubeadm.yaml


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

&lt;/div&gt;

&lt;p&gt;Verify the successful application of changes to the configuration with the command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system get configmap kubeadm-config &lt;span class="nt"&gt;-o&lt;/span&gt; yaml


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

&lt;/div&gt;




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

&lt;p&gt;In the dynamic world of Kubernetes, ensuring secure access to the API server is a critical aspect of maintaining a robust and reliable infrastructure. By carefully updating and fine-tuning the TLS certificates used by the Kubernetes API server, administrators can seamlessly integrate new IP addresses or hostnames into the certificate's Subject Alternative Names (SANs), thus enabling secure and uninterrupted access. However, it is crucial to follow the outlined steps diligently and verify the changes to guarantee a smooth transition without compromising the security of the Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and I hope someone finds this helpful. I am happy to chat further about new knowledge, opportunities and possible corrections. I can be reached on twitter via, @oluseyeo_&lt;/p&gt;

&lt;p&gt;Happy Hacking 💥 💥&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>security</category>
      <category>sitereliabilityengineering</category>
      <category>devops</category>
    </item>
    <item>
      <title>PASSING THE AWS SOLUTIONS ARCHITECT – ASSOCIATE EXAM SAA-C02 IN 30 DAYS.</title>
      <dc:creator>oluseyeo</dc:creator>
      <pubDate>Mon, 29 Nov 2021 12:12:00 +0000</pubDate>
      <link>https://forem.com/oluseyeo/passing-the-aws-solutions-architect-associate-exam-saa-c02-in-30-days-5hnb</link>
      <guid>https://forem.com/oluseyeo/passing-the-aws-solutions-architect-associate-exam-saa-c02-in-30-days-5hnb</guid>
      <description>&lt;p&gt;Writing about my experience especially when it has to do with exams is not what I’d normally do. However, following the various feedback I got following my success at the ‘AWS Solutions Architect – Associate’ exam (“SAA-C02”, “CSAA”, or “the Exam”), I am, therefore, compelled to share this article to assist future test-takers in preparation for the Exam. &lt;/p&gt;

&lt;p&gt;The AWS Solutions Architect – Associate exam, is one of the most popular certification exams in the suite of proficiency tests offered by AWS - the biggest public cloud service provider in the world. The exam tests for proficiency in and understanding of how the different services of AWS can be architected to build scalable and resilient infrastructure. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/certification/certified-solutions-architect-associate/"&gt;&lt;br&gt;
  &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zBmxvf_g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njqhhdrnue85ooavytd4.png" alt="AWS-SAA-C02 exam link" width="340" height="340"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of the associate level Cloud architecture exams I have taken, (&lt;em&gt;Azure, Google Cloud &amp;amp; Alibaba&lt;/em&gt;), I found AWS’s – at the time of writing the exam, the most difficult of the four. With better preparation than I did, - 30 days of study in total, I reckon my judgment of the AWS-SAA-C02 would be better.&lt;/p&gt;

&lt;p&gt;Why 30 days? Well, I had less time due to work and other life events. Therefore, I had to leverage my wealth of knowledge and experience of the other Cloud computing services to run through my study within the short period.&lt;/p&gt;

&lt;h4&gt;
  
  
  EXAM RECOMMENDATION
&lt;/h4&gt;

&lt;p&gt;It is highly recommended that test-takers have at least one year of working or designing applications within the Public Cloud services – most preferably, AWS, prior to sitting for the AWS-CSAA exam. For noobs who are just making their foray into the AWS and Cloud Computing space in general, I’d recommend at least 6 months of study, or taking the AWS Cloud Practitioner exam first, as it is better suited as an introductory exam. It is the perfect foundational exam to test for knowledge of key AWS services before dipping two feet in service architecture.&lt;/p&gt;




&lt;h3&gt;
  
  
  THE ACTION ITSELF
&lt;/h3&gt;

&lt;p&gt;The AWS-CSAA tests for knowledge across (4) major domains namely;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Designing Resilient Architecture&lt;/li&gt;
&lt;li&gt;Designing High Performing Architectures&lt;/li&gt;
&lt;li&gt;Designing Secure Applications &amp;amp; Architectures&lt;/li&gt;
&lt;li&gt;Designing Cost Optimized Architectures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These pillars sum the core of what the AWS- SAA-C02 exam is built on. &lt;/p&gt;

&lt;p&gt;For study purposes, however, I shall not bore you with the standardized nomenclatures, and would rather reclassify these domains into core reading areas – made up of services the test is built on. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A popular reference in the Developer (Technology) community is how professionals rely less on official documentation (maybe because they are considered too abstract), but more on shared practical knowledge from seniors and colleagues in the industry. This is what has necessitated the popularity of developer blogs and the greatest of all, Stackoverflow.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My reclassification of the four domains, and concepts to masters are into these 10 areas respectively;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1.   SECURITY&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;IAM Users, Roles, Group &amp;amp; Policy, Securing Root User Account, Role Assignment - STS &amp;amp; metadata, Certificate Manager, Parameter store, Secrets Manager, KMS and encryption, CloudHSM, Macie for PII, Presigned S3 URLs, AWS WAF, GuardDuty &amp;amp; Shield for DDOS. The network layers at which these services operate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2.   NETWORKING&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;VPC provisioning – how subnets come to play, differences between Security Group &amp;amp; NACL, NAT Gateway and how private subnets connect to it, PrivateLink, Peering, DirectConnect, CloudHub and TransitGateway Connection options for connections between AWS Services and how to connect Onprem resources.&lt;/li&gt;
&lt;li&gt;Routing with Route53 – the different routing policies and when to use them. &lt;/li&gt;
&lt;li&gt;Load Balancers – how they work in sync with other AWS services, and external traffic routing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3.   COMPUTE &amp;amp; SERVERLESS&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Launching EC2, AMI– types and selection, how to save cost with Reserved instances and differentiate from Spot instances. Leveraging Spot Fleets, EC2 Roles, and Placement Groups. Difference between Metadata and UserData.&lt;/li&gt;
&lt;li&gt;What Lambda functions are and how to trigger it, differentiating between what EKS and ECS are, and what Fargate does.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4.   STORAGE&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The storage types and lifecycle method – one-way traffic to Glacier Storage. Object versioning, backup and its rules. How to host static websites with S3 and integrate Cloudfront. Object lock and different modes to meet regulations.&lt;/li&gt;
&lt;li&gt;Volume Storage: Differences between Block Storage and File System. The different types of the Elastic File System. How to encrypt and unencrypt attached volumes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;5.    DATABASE &amp;amp; CACHING&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Categorize DB’s between OLTP and OLAP; SQLs and NoSQLs. Understand the various DB engines available, and Amazon’s proprietary DB, Aurora and the edge it has over other DBs, DB Caching, Difference between Read-Replicas and Secondary Tables, Failover in a Multi-regional setup. How to build highly-available and highly-performant DB that can either scale vertically or horizontally.&lt;/li&gt;
&lt;li&gt;How to setup Elastic Cache, the role of DAX on DynamoDBs and the advantage Global Accelerator affords service architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;6.  BIG DATA &amp;amp; IOT&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The roles of Redshift, Athena, ElasticSearch, and Kinesis in Data processing and streaming. Be able to differentiate between the streaming options in Kinesis – near real-time, and real-time, and the list of AWS services Firehouse streams to.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  &lt;strong&gt;7. AUTOMATION &amp;amp; DECOUPLING WORKFLOWS&lt;/strong&gt;
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;SNS, SQS. How DLQ is setup and its purpose to message processing. &lt;/li&gt;
&lt;li&gt;Identify the various types of Message ordering, its size, limits per second per category, and the role of SNS in the Pub-Sub message delivery model.&lt;/li&gt;
&lt;li&gt;What is CloudFormation, Stacks and its lifecycle. Knowledge of Elastic Beanstalk will come in handy as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;8.  MIGRATION&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;How to migrate data with the SNOW options – size, and realistic migration timelines. Storage Gateway &amp;amp; DataSync.&lt;/li&gt;
&lt;li&gt;Understand how to optimize data transfers from on-prem to the Cloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;9.  MONITORING &amp;amp; LOGGING&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Cloud watch events, Cloud Watch Logs and storage options. Differentiation between default, and custom metrics.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;10.  COMPLIANCE AND GOVERNANCE&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt; What AWS Organizations is, and the role is managing sub-accounts, AWS Tags and Resource Groups. &lt;/li&gt;
&lt;li&gt;Auditing with Trusted Advisor, Budget Management with Budgets &amp;amp; Costs explorer, AWS Config and AWS Systems Manager in managing compliance across instances,&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  NOTE!:
&lt;/h4&gt;

&lt;p&gt;While the above isn’t exhaustive, the questions I encountered during the exam required a clear understanding of the aforementioned services. &lt;/p&gt;

&lt;p&gt;In addition, note that AWS is heavy on 3 core components that serves as the base for most of the questions, and without which, your journey towards being a Cloud Solutions architect is considered half-baked. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;i. High Availability:&lt;/strong&gt; The system lifecycle must not have a single point of failure, and should continue to function, even in a degraded state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ii. Fault Tolerance:&lt;/strong&gt; The system must operationally function, despite some or complete component failure, that may include a zone/regional blackout. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iii.  Cost Optimization:&lt;/strong&gt; AWS is highly keen on cost-efficient service architectures. Thus, as you architect solutions that are considered highly available and performant, they must be at the lowest price point possible.&lt;/p&gt;




&lt;h3&gt;
  
  
  STUDY RESOURCES:
&lt;/h3&gt;

&lt;p&gt;I primarily used 3 resources for the exam. Kindly note that this isn’t an exhaustive list, as I flickered through many resources randomly to find the best fit for my study style.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://learn.acloud.guru/course/certified-solutions-architect-associate/dashboard"&gt;Acloud Guru&lt;/a&gt; – Kudos to the ACG team for the course compilation. Both the content, exam tips, and practice tests were helpful to grasping the needed knowledge for the exam. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/c/amazonwebservices/search?query=re%3Ainvent"&gt;AWS re:Invent Sessions on YouTube&lt;/a&gt; – This was my audio through my daily commute. So I simply made a playlist of re:invent sessions and listened while in transit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.amazon.com/Certified-Solutions-Architect-Study-Guide/dp/111950421X"&gt;AWS Certified Solutions Architect Study Guide&lt;/a&gt;  – this book was written by Ben Piper, David Clinton, and was a game-changer for me in understanding some concepts beyond the surface level. One out of many concepts was understanding the Read and Write throughput capacity for DynamoDB. Also noteworthy are the tests at the end of each chapter to check the understanding of each concept.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Last but not least is hands-on experience in the AWS Management console – I urge all test takers to take advantage of the 12 months (1 Year) free tier offering from AWS. With this personal free tier account, you can access most AWS services at no cost, if you stay within the limits of the offering. AWS offers as much as 750 FREE monthly hours for EC2.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see the full details of the services covered &lt;a href="https://aws.amazon.com/free/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/free/"&gt;&lt;br&gt;
  &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JOKAbQZ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/an8szn4pgd9im42jq7te.png" alt="AWS-Free-Tier-Account-Offerings" width="800" height="800"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Never discount Practice tests – take them as though it was the real exam to see what area you need to brush up knowledge on. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Post-Exam
&lt;/h4&gt;

&lt;p&gt;Finally, finishing the certification is just the beginning of the AWS journey for me.  AWS services are intriguing, and I’m studying further to deepen my knowledge for DevOps and shortly, the Solutions Architect Professional exam.  Therefore, the journey to Cloud Infrastructure mastery, particularly, how to help businesses leverage cloud service offerings to improve agility and ultimately gain a business advantage is a life-long learning journey for me. &lt;/p&gt;

&lt;h4&gt;
  
  
  In Conclusion
&lt;/h4&gt;

&lt;p&gt;In a future blog post, I shall write in more detail on the key knowledge to acquire on each of the pillars that make up the exam. In the interim, I am happy to connect and answer any additional questions you may have about the SAA-C02 exam and/or speak at community sessions - sharing knowledge about Public Cloud Infrastructure. You may also leave your questions and comments below, and I’ll get back to answering them as soon as possible.&lt;/p&gt;

&lt;p&gt;You may also connect with me via Twitter &lt;a href="https://twitter.com/oluseyeo_"&gt;@oluseyeo_&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/oluwaseyeolofinyo/"&gt;LinkedIn - Oluwaseye&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Best of luck, and keep building!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/eH986DlVKgHGfOmZ2w/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/eH986DlVKgHGfOmZ2w/giphy.gif" alt="image" width="480" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>solutionsarchitecture</category>
    </item>
    <item>
      <title>How to Create Relationships with Mongoose and Node.JS</title>
      <dc:creator>oluseyeo</dc:creator>
      <pubDate>Tue, 15 Sep 2020 05:53:05 +0000</pubDate>
      <link>https://forem.com/oluseyeo/how-to-create-relationships-with-mongoose-and-node-js-11c8</link>
      <guid>https://forem.com/oluseyeo/how-to-create-relationships-with-mongoose-and-node-js-11c8</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;FOCUS: One-to-many Relationships&lt;/strong&gt;
&lt;/h2&gt;




&lt;p&gt;NoSQL databases, unlike SQL databases like PostgreSQL, MYSQL etc, which are traditionally built for data relationship management, indexed and referenced across multiple tables, have a poor or almost non-existent support for relationships in her JSON-like built schema.  MongoDB, a popular NoSQL database, like others, have inbuilt methods that developers can leverage to build relationships between multiple schemas. &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%2Fmedia.geeksforgeeks.org%2Fwp-content%2Fcdn-uploads%2F20191104165821%2FSQL-Vs-NoSQL1.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%2Fmedia.geeksforgeeks.org%2Fwp-content%2Fcdn-uploads%2F20191104165821%2FSQL-Vs-NoSQL1.png" alt="SQL vs NoSQL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Relationships in MongoDB are built on the JOIN functionality and with the popular NPM module, the Mongoose library, developers can harness its raw power, building complex relationships, and importantly, designing efficient databases to avoid throttling queries, as it would have been done, if working with an SQL database.&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%2Fs3.ap-south-1.amazonaws.com%2Fafteracademy-server-uploads%2Fmastering-mongoose-for-mongodb-and-node-js-mongoose-diagram-77560014632570f4.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%2Fs3.ap-south-1.amazonaws.com%2Fafteracademy-server-uploads%2Fmastering-mongoose-for-mongodb-and-node-js-mongoose-diagram-77560014632570f4.png" alt="Mongoose, MongoDB, NodeJS relationship chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, I am going to be touching on the following in details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Types of relationships &amp;amp; object reference types in MongoDB&lt;/li&gt;
&lt;li&gt;  Mongoose Populate Method&lt;/li&gt;
&lt;li&gt;  Mongoose Virtuals&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is expected that readers have a good basic grasp of ExpressJS, Mongoose, ES6+ JS &amp;amp; Postman.&lt;/p&gt;

&lt;p&gt;Also, the following should be available either as a service or installed and running locally on your PC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  MongoDB or you can choose Atlas, the cloud version of MongoDB.&lt;/li&gt;
&lt;li&gt;  Mongoose NPM. Simply run [npm i mongoose ]  at the root of your project folder.&lt;/li&gt;
&lt;li&gt;  Postman, to test the endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npm i mongoose&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;For the purpose of this write-up, I have built a small &lt;em&gt;“Publishing House”&lt;/em&gt; project, to walk you through how to achieve any of the methods to be discussed. The Publishing House project assumes Publishers as registered users, who can publish multiple books under their portfolio.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;MongoDB as database.&lt;/li&gt;
&lt;li&gt;Mongoose library, as the database object document manager (ODM).&lt;/li&gt;
&lt;li&gt;ExpressJS to create our routes using async/await ES6+ since we shall be dealing with promises.&lt;/li&gt;
&lt;li&gt;Postman will be used to test our endpoints for responses.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Mongoose&lt;/strong&gt; represents relational data using two major design models, and the choice of model to deploy when planning the database collections of any project is predominantly hinged on the data-size, data accuracy, and frequency of access. Nonetheless, the rule of thumb is, the size of documents stored, is in direct proportion to the speed at which queries are resolved, and ultimately, how performant the database is.&lt;/p&gt;

&lt;p&gt;The two models are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embedded Data Models [Denormalization]:&lt;/strong&gt; This is the least recommended form of relationship. Data is simply denormalized by embedding Child (related) documents right into the Parent (main) document. Using our “Publishing project” as an example, this would mean, Publishers, store all published books and related information directly on each publisher’s object.&lt;br&gt;
In a typical One-to-Few document relationship, this would work perfectly as the expected size of documents is not more than 20. However, when working with Child documents of a larger size, this size heavily impairs database performance, causing lags, and difficulty in keeping data synced, ultimately bringing about poor user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Referenced Data Model [Normalization]:&lt;/strong&gt; When data is normalized, it means documents are separated into different collections, and they share references between each other. In most cases, a single update on the Parent document, with all parameters passed, updates the child documents directly referenced to it. The rest of this tutorial will be focused on the best use case of this method, and how best to organize our database collections and documents in an efficient manner.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Referencing documents between collections can be done via dual approaches, and are as follows: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Child Referencing:&lt;/strong&gt; A document is considered Child referenced, when the Parent document stores a reference to its child collections, storing its identifiers - in most situations, the id, in an array of similar identifiers on the Parent document. Citing our “Publishing House” project, this would mean, having Publishers store the book._id for each book created, in an array of book id’s, predefined on the Publisher's Schema, and when needed, fetch these child documents using the populate method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From our Project, see the Publisher's schema below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publisherSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Schema&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;publishedBooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Book&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Publisher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publisherSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;
 Publisher Schema [Notice published books is an array] 



&lt;p&gt;Here is our Book Schema:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bookSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Schema&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;publishYear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Publisher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Book&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bookSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Book Schema&lt;/p&gt;

&lt;p&gt;The mongoose “populate” method loads the details of each referenced Child documents and returns it alongside each Publisher's document fetched from the DB. Let’s see an example of this using our project.&lt;/p&gt;

&lt;p&gt;We start by creating a new Publisher below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/***
 * @action ADD A NEW PUBLISHER
 * @route http://localhost:3000/addPublisher
 * @method POST
*/&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/addPublisher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//validate req.body data before saving&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publisher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;publisher&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;});&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;
Create a new publisher



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
    "success": true,
    "data": {
        "publishedBooks": [],
        "_id": "5f5f8ac71edcc2122cb341c7",
        "name": "Embedded Publishers",
        "location": "Lagos, Nigeria",
        "createdAt": "2020-09-14T15:22:47.183Z",
        "updatedAt": "2020-09-14T15:22:47.183Z",
        "__v": 0
    }
}


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

&lt;/div&gt;
A new publisher



&lt;p&gt;Next, the newly created Publisher proceeds to add a new book about to publish to it's DB. The publisher’s _id is passed in as a value to the Publisher’s key on the Book schema before saving, and in the same request loop, right after calling the save method on the new book, the newly created book object returned from the Promise, MUST be passed as a parameter to a push method, called on the Publisher’s key. This would ensure that the book object, is saved on the Publisher's document.&lt;/p&gt;

&lt;p&gt;Here's the magic breakdown:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/***
 * @action ADD A NEW BOOK
 * @route http://localhost:3000/addBook
 * @method POST
*/&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/addBook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="cm"&gt;/**
    * @tutorial: steps
    * 1. Authenticate publisher and get user _id.
    * 2. Assign user id from signed in publisher to publisher key.
    * 3. Call save method on Book.
   */&lt;/span&gt;

   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//validate data as required&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// book.publisher = publisher._id; &amp;lt;=== Assign user id from signed in publisher to publisher key&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="cm"&gt;/**
       * @tutorial: steps
       * 1. Find the publishing house by Publisher ID.
       * 2. Call Push method on publishedBook key of Publisher.
       * 3. Pass newly created book as value.
       * 4. Call save method.
      */&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publisher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&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="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishedBooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;//return new book object, after saving it to Publisher&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;})&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;
A Publisher adding a new book to be published to her DB



&lt;p&gt;This is the defined way to saving child document references(id’s) on the publisher’s document. On successful creation, the below is returned when you query the Publisher's id. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; &lt;em&gt;The Publisher below created 3 new books.&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
    "publishedBooks": [
        {
            "_id": "5f5f8ced4021061030b0ab68",
            "name": "Learn to Populate virtuals Mongoose",
            "publishYear": 2019,
            "author": "Devangelist"
        },
        {
            "_id": "5f5f8d144021061030b0ab6a",
            "name": "Why GoLang gaining traction",
            "publishYear": 2020,
            "author": "John Doe"
        },
        {
            "_id": "5f5f8d3c4021061030b0ab6b",
            "name": "Developer Impostor syndrome",
            "publishYear": 2021,
            "author": "John Mark"
        }
    ],
    "_id": "5f5f8ac71edcc2122cb341c7",
    "name": "Embedded Publishers",
    "location": "Lagos, Nigeria",
    "createdAt": "2020-09-14T15:22:47.183Z",
    "updatedAt": "2020-09-14T15:33:16.449Z",
    "__v": 3
}


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

&lt;/div&gt;
 Saved object returns Child array



&lt;p&gt;However, Should the push and save method not be called on the Publisher's document, the Publisher although existing, and the new Book created, will return an empty array of publishedBooks as seen below, when queried.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
    "success": true,
    "data": {
        "publishedBooks": [],
        "_id": "5f5f8ac71edcc2122cb341c7",
        "name": "Embedded Publishers",
        "location": "Lagos, Nigeria",
        "createdAt": "2020-09-14T15:22:47.183Z",
        "updatedAt": "2020-09-14T15:22:47.183Z",
        "__v": 0
    }
}


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

&lt;/div&gt;
Empty Array, when object isn't pushed and saved



&lt;p&gt;Despite the success of the Child Referencing method, its limitation as seen above is that the size of the array of Id’s can get very large quickly, consequently seeing the database lose efficiency and performance overtime as the size of the array grows. &lt;a href="https://docs.mongodb.com/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/" rel="noopener noreferrer"&gt;MongoDB officially recognizes this as an anti-pattern, and strongly discourages its use&lt;/a&gt; for document relationships run at scale.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parent Referencing:&lt;/strong&gt;  Parent referencing, on the other hand, is a tad different from Child Referencing as described earlier, in that, ONLY Child documents keep a reference to parent documents. This reference is singly kept on each Child document created, defined as an object ID on the Schema. Parent documents, conversely, keep no direct reference but builds one with the help of a Mongoose method called Virtuals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://mongoosejs.com/docs/populate.html#populate-virtuals" rel="noopener noreferrer"&gt;Mongoose Virtual&lt;/a&gt; is a far more sophisticated approach to fetching referenced Child documents, and it importantly, takes up less memory for data storage, as the new key-field Mongoose virtual creates whenever a query is run, doesn’t persist on the Parent document. Occasionally, Virtuals are also referred to as "reverse-populate', as such, when you hear people mention that, don't fret!&lt;/p&gt;

&lt;p&gt;Enough with the talk, let's jump into our project code.&lt;br&gt;
First, let's see what our Book Schema looks like below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bookSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Schema&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;publishYear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Publisher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Book&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bookSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Next, which is where the tricky part lies, is our Parent document. Please pay attention to how virtuals are defined and a crucial part of this is the extra options we must set on the Schema, without which no results get returned. These extra options are the &lt;em&gt;toJSON&lt;/em&gt; and &lt;em&gt;toObject&lt;/em&gt; options. They both default to false, and are core to ensuring that whenever the Parent document is queried when these options are set to True, results are passed to the .json() method on the response call.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publisherSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Schema&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @action Defined Schema Virtual
 * @keys 
 *    1.   The first parameter can be named anything.
 *          It defines the name of the key to be named on the Schema
 * 
 *    2. Options Object
 *       ref: Model name for Child collection
 *       localField: Key for reference id, stored on Child Doc, as named on Parent Doc.
 *       foreignField: Key name that holds localField value on Child Document
 */&lt;/span&gt;
&lt;span class="nx"&gt;publisherSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;virtual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;booksPublished&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Book&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//The Model to use&lt;/span&gt;
   &lt;span class="na"&gt;localField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//Find in Model, where localField &lt;/span&gt;
   &lt;span class="na"&gt;foreignField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;publisher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// is equal to foreignField&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Set Object and Json property to true. Default is set to false&lt;/span&gt;
&lt;span class="nx"&gt;publisherSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;toObject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;virtuals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;publisherSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;toJSON&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;virtuals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;


&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Publisher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publisherSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;
Notice that we don’t have a publishedBooks array anymore on the Schema






&lt;p&gt;&lt;strong&gt;Defining the virtual object comes next, and the best way to easily remember how to define it, (&lt;em&gt;much easier if you’re from an SQL background&lt;/em&gt;), is;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SELECT “name for the virtual field” FROM “&lt;em&gt;ref&lt;/em&gt; – Child collection name”, WHERE “&lt;em&gt;localField&lt;/em&gt; – Parent key stored on child collection, mostly &lt;em&gt;id” EQUALS “_foreignField&lt;/em&gt; – the name of Child schema key, storing parent id, as its value.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;With both options above defined, whenever we populate our Publisher after calling the GET method, we are guaranteed to retrieve all books published by each publisher, and for further specificity, as not all the information about a book will be needed, select the keys required from each book and return it in the response body. &lt;/p&gt;

&lt;p&gt;See how it is done in our project below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/***
 * @action GET ALL PUBLISHERS
 * @route http://localhost:3000/publishers
 * @method GET
 */&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/publishers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;populate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;booksPublished&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name publishYear author&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;});&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;
Get all Publishers



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;_id&lt;/span&gt;&lt;span class="dl"&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;5f5f546e190dff51041db304&lt;/span&gt;&lt;span class="dl"&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;name&lt;/span&gt;&lt;span class="dl"&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;Random Publishers&lt;/span&gt;&lt;span class="dl"&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;location&lt;/span&gt;&lt;span class="dl"&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;Kigali, Rwanda&lt;/span&gt;&lt;span class="dl"&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;createdAt&lt;/span&gt;&lt;span class="dl"&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;2020-09-14T11:30:54.768Z&lt;/span&gt;&lt;span class="dl"&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;updatedAt&lt;/span&gt;&lt;span class="dl"&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;2020-09-14T11:30:54.768Z&lt;/span&gt;&lt;span class="dl"&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;__v&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;booksPublished&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;_id&lt;/span&gt;&lt;span class="dl"&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;5f5f548e190dff51041db305&lt;/span&gt;&lt;span class="dl"&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;name&lt;/span&gt;&lt;span class="dl"&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;Mastering Mongoose with Javascript&lt;/span&gt;&lt;span class="dl"&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;publishYear&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="dl"&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;Devangelist&lt;/span&gt;&lt;span class="dl"&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;publisher&lt;/span&gt;&lt;span class="dl"&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;5f5f546e190dff51041db304&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="p"&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;_id&lt;/span&gt;&lt;span class="dl"&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;5f5f55ca190dff51041db307&lt;/span&gt;&lt;span class="dl"&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;name&lt;/span&gt;&lt;span class="dl"&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;Learning Mongoose Populate method&lt;/span&gt;&lt;span class="dl"&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;publishYear&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="dl"&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;Devangelist&lt;/span&gt;&lt;span class="dl"&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;publisher&lt;/span&gt;&lt;span class="dl"&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;5f5f546e190dff51041db304&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="p"&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;id&lt;/span&gt;&lt;span class="dl"&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;5f5f546e190dff51041db304&lt;/span&gt;&lt;span class="dl"&gt;"&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;
Query results from getting all publishers [Notice the booksPublished array]



&lt;p&gt;Summarily, Parent referencing is the best approach to referencing when using the Normalized model method and dealing with a large dataset.&lt;/p&gt;

&lt;p&gt;If you made it to this point, thank you for reading through, and I hope you’ve learnt something-[new]. I’m happy to chat further about new knowledge, opportunities and possible corrections. I can be reached on twitter via, &lt;a href="https://twitter.com/oluseyeo_" rel="noopener noreferrer"&gt;@oluseyeo_&lt;/a&gt; or via email at, &lt;a href="//mailto:sodevangelist@gmail.com"&gt;sodevangelist@gmail.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Hacking 💥 💥&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL: DR;&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There are two modelling approaches, Embedded and Referenced.&lt;/li&gt;
&lt;li&gt;Embed only when your data will be accessed less frequently and you’re mostly only reading data.&lt;/li&gt;
&lt;li&gt;For larger IOPS, use referencing model.&lt;/li&gt;
&lt;li&gt;Referencing can be done in two ways, Child and Parent referencing.&lt;/li&gt;
&lt;li&gt;If Child document size is small, under 100, use Child referencing. This stores child reference key directly on Parent document using the push method. &lt;/li&gt;
&lt;li&gt;If the size of Child documents is huge, use the parent referencing option, reverse populating Parent documents using mongoose virtual.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Recommended further reading:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.mongodb.com/manual/applications/data-models/" rel="noopener noreferrer"&gt;Data Access Patterns&lt;/a&gt;&lt;br&gt;
&lt;a href="https://mongoosejs.com/docs/populate.html" rel="noopener noreferrer"&gt;Mongoose Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design-part-3" rel="noopener noreferrer"&gt;Denormalization&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>node</category>
      <category>javascript</category>
      <category>database</category>
    </item>
    <item>
      <title>HTML, CSS &amp; Javascript good enough for a Newbie?</title>
      <dc:creator>oluseyeo</dc:creator>
      <pubDate>Sat, 16 May 2020 12:06:25 +0000</pubDate>
      <link>https://forem.com/oluseyeo/html-css-javascript-good-enough-for-a-newbie-m5e</link>
      <guid>https://forem.com/oluseyeo/html-css-javascript-good-enough-for-a-newbie-m5e</guid>
      <description>&lt;p&gt;&lt;strong&gt;HTML, CSS with a sprinkle of Javascript, will likely not land you your first role as a Developer, in 2020.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With this, I welcome you to my first public article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oB--QGjJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/kk2f027irkwflxn5dzyn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oB--QGjJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/kk2f027irkwflxn5dzyn.png" alt="tweet-screengrab" width="603" height="482"&gt;&lt;/a&gt;&lt;/p&gt;
This tweet made rounds on #techTwitter, and most persons mistook Danny for a gatekeeper who was raising the entry-level bar for noobs.



&lt;p&gt;Smacking a few pixels around, and being able to lay them out properly on a page, to be displayed in browsers about a decade ago, would land you, your first job as a Web-Developer. Unfortunately, it isn't so anymore!&lt;br&gt;
At the time, it wasn’t expedient to know more for starters, as most firms had a training culture for Juniors, and in their ranks also, were senior developers who were committed to training and retraining, which ultimately, would result in your professional growth as a noob.&lt;/p&gt;

&lt;p&gt;Circa 5years back, this training culture for junior developers, create a gap that resulted in the Bootcamp boom experienced around the world. This, saw to companies outsourcing a huge chunk of their internal training programs to Bootcamps, trusting their expertise to train committed and passionate noobs to become world-class developers, who can be hired.&lt;/p&gt;

&lt;p&gt;This background story isn’t to share the evolution and proliferation of Web programming but to share insights on how events have evolved to this point where, HTML, CSS and a bit of Javascript would not open the doors so easily anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I bet you’re asking, what should I know?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As it stands, there’s no general consensus on what stack or knowledge depth is sufficient, however going by statistics as gleaned from open job positions, it’s expected to know the following depending on your pick of the development-side.&lt;/p&gt;

&lt;p&gt;At base, you are to know&lt;br&gt;
⁃ HTML5&lt;br&gt;
⁃ CSS3&lt;br&gt;
⁃ Javascript(ES6) - Arrow functions shouldn't be alien to you.&lt;br&gt;
⁃ GIT (Github/Gitlab)&lt;br&gt;
⁃ Command Prompt&lt;/p&gt;

&lt;p&gt;From this point, you can then choose the side to fight for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xg4wzuA8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.pinimg.com/originals/73/91/60/739160fb98686642ce26af5e12b5347c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xg4wzuA8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.pinimg.com/originals/73/91/60/739160fb98686642ce26af5e12b5347c.jpg" alt="frontend-developer" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you choose Client-Side Development(&lt;strong&gt;Frontend&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;⁃ React or Vue or Angular. &lt;br&gt;
⁃ Preferably, a testing framework alongside.&lt;/p&gt;

&lt;p&gt;➡️ Read more about the Frontend Developer Roadmap for 2020 &lt;a href="https://roadmap.sh/frontend"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Server-Side Development(&lt;strong&gt;Backend&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;⁃ PHP (&lt;em&gt;Laravel&lt;/em&gt;) or NodeJs(&lt;em&gt;Express&lt;/em&gt;) or Python(&lt;em&gt;Django&lt;/em&gt;)&lt;br&gt;
⁃ Database ( SQL &amp;amp; No-SQL; &lt;em&gt;most tend to pick MongoDB or MySQL&lt;/em&gt;)&lt;br&gt;
⁃ Preferably, a testing framework alongside.&lt;/p&gt;

&lt;p&gt;➡️ Read more about the Backend Developer Roadmap for 2020 &lt;a href="https://roadmap.sh/backend"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fullstack:&lt;br&gt;
⁃ Everything above.&lt;br&gt;
⁃ AWS or GCP (the basics)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Before jumping into any framework, please understand, NOT cram, the basics of the language.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As I bring this to a close, here's a list of awesome resources that have helped me so far.&lt;br&gt;
&lt;strong&gt;PS:&lt;/strong&gt; &lt;em&gt;This isn't an exhaustive list, neither am I affiliated with any of the tutors&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;⁃ &lt;a href="https://www.freecodecamp.org"&gt;FreecodeCamp&lt;/a&gt;: Always grateful for Quincy's vision to start FreecodeCamp. That is the ultimate learning resource for beginners.&lt;/p&gt;

&lt;p&gt;⁃ &lt;a href="https://www.udemy.com/user/4b4368a3-b5c8-4529-aa65-2056ec31f37e/"&gt;Dr Angela Yu&lt;/a&gt;:  Angela, is so resourceful. She simplifies the concept behind every step and I highly recommend her lectures. I look forward to meeting her someday, so I can at least buy her coffee and say, thank you.&lt;/p&gt;

&lt;p&gt;⁃ &lt;a href="https://www.youtube.com/channel/UC29ju8bIPH5as8OGnQzwJyA"&gt;Brad Traversy&lt;/a&gt;: Please, follow his YouTube channel if you cannot afford to buy &lt;a href="https://www.udemy.com/user/brad-traversy"&gt;his courses&lt;/a&gt; yet. Brad is mindblowing!&lt;/p&gt;

&lt;p&gt;⁃ &lt;a href="https://www.codecademy.com/learn"&gt;Codecademy&lt;/a&gt;: They’ll get your hands dirty with so many projects that will up your skill.&lt;/p&gt;

&lt;p&gt;⁃ &lt;a href="https://www.youtube.com/channel/UCSJbGtTlrDami-tDGPUV9-w"&gt;Maximilian Schwarzmuller&lt;/a&gt;: He goes an extra mile to ensure that his student understands the art. He puts up courses on &lt;a href="https://www.udemy.com/user/maximilian-schwarzmuller"&gt;Udemy.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If there are other resources that have been helpful to you, please do not hesitate to suggest them in the comments section.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusively,&lt;/strong&gt; remember, starting out with an easier language will make it simpler to master complex languages later. As your career progress, you'll get to master not only one but multiple languages, as it'll deepen your versatility and success in any programming environment you find yourself.&lt;/p&gt;

&lt;p&gt;With love ❤️&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Olu the Devangelist!&lt;/em&gt; #Reskill&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>codenewbie</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
