<?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: Adisakshya Chauhan</title>
    <description>The latest articles on Forem by Adisakshya Chauhan (@adisakshya).</description>
    <link>https://forem.com/adisakshya</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%2F480580%2F7f1154b4-702b-4b7f-a6a1-dbcabf063b05.jpg</url>
      <title>Forem: Adisakshya Chauhan</title>
      <link>https://forem.com/adisakshya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/adisakshya"/>
    <language>en</language>
    <item>
      <title>Setting up an API Gateway for your Microservices</title>
      <dc:creator>Adisakshya Chauhan</dc:creator>
      <pubDate>Sun, 17 Jul 2022 09:13:26 +0000</pubDate>
      <link>https://forem.com/adisakshya/setting-up-an-api-gateway-for-your-microservices-3gal</link>
      <guid>https://forem.com/adisakshya/setting-up-an-api-gateway-for-your-microservices-3gal</guid>
      <description>&lt;p&gt;In the previous article from the &lt;a href="https://dev.to/adisakshya/series/16626"&gt;API Gateway 101 Series&lt;/a&gt; we saw an &lt;a href="https://dev.to/adisakshya/introduction-to-api-gateway-1fpo"&gt;Introduction to API Gateway&lt;/a&gt;, understanding the basic know-how. Now it’s time to see an API gateway in action in front of multiple microservices.&lt;/p&gt;

&lt;p&gt;We’ll be spinning up multiple mock microservices within a private network (docker network) making them inaccessible from the host. The API gateway will be accessible from the host and all the microservices will be attached to the gateway. Thus any client request needs to go through the gateway first before being served by the actual microservice in the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;We’ll be deploying the API gateway and the mock microservices using Docker. If you’re not familiar with Docker &amp;amp; Docker Compose then here is a quick link to understand the basics - &lt;a href="https://docs.docker.com/get-started/"&gt;Docker Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For exploring and utilizing the Kong Admin API we'll be using Postman Collections.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Postman is an API Client that assists to create, share, test and documenting APIs.&lt;/li&gt;
&lt;li&gt;Postman collections are basically a group of saved API requests that are executable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll see that I’ve organized the postman collection for this series in folders making it easy for you to explore saved APIs section-wise. Postman isn’t necessary for this series, you can easily replicate the saved APIs into your favourite API client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I’m using Docker installed on a windows machine. In this article where ever I mention ‘192.168.99.100’, it refers to the ‘docker-machine ip’.&lt;/p&gt;

&lt;p&gt;Cool, now that once Docker, Docker Compose &amp;amp; Postman are installed, we can move forward to get started. Let’s now get our hands dirty by spinning up some mock microservices and setting up an API Gateway in front of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spinning up mock microservices
&lt;/h2&gt;

&lt;p&gt;The source code for the mock microservices created for this article can be found on my GitHub repository - &lt;a href="https://github.com/adisakshya/api-gateway"&gt;adisakshya/api-gateway&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get started let’s clone the git repository -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/adisakshya/api-gateway
&lt;span class="nb"&gt;cd &lt;/span&gt;api-gateway/single-node-kong-gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source code for each mock microservice lives in the respective directory named after them. Some services are defined directly in the docker-compose file. All the microservices and the API gateway can be started at once using the following command -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start the respective docker containers in the background of your terminal (aka docker detached mode). You can check the list of containers that are up using the following command -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker ps -a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rq_X6HbU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/spinning-up-microservices-using-docker.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rq_X6HbU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/spinning-up-microservices-using-docker.png" alt="List of Docker containers" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we’re running docker containers in the detached mode we’ll not be able to see any logs. But If you want to see logs of a specific container you can use the following command -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker container logs '&amp;lt;&amp;lt;CONTAINER_ID&amp;gt;&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IP for the internal docker gateway is 172.18.0.1, to get the IP for a particular docker container you can use the following command -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker inspect '&amp;lt;&amp;lt;CONTAINER_ID&amp;gt;&amp;gt;' | tail -n 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve used the tail command to see the IP address of the docker container, which is found towards the end of the response from the docker inspect command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dtjCSLc5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/inspecting-ip-address-of-a-microservice.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dtjCSLc5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/inspecting-ip-address-of-a-microservice.png" alt="inspecting-ip-address-of-a-microservice.png" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll need the IPs of the docker containers to attach these microservices to our Kong API gateway. By now you’ll have the following containers up and running -&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Container Name&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;IP (Docker Network)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;IP (Host Network)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Konga (Admin GUI)&lt;/td&gt;
&lt;td&gt;172.19.0.2&lt;/td&gt;
&lt;td&gt;192.168.99.100:1337&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kong&lt;/td&gt;
&lt;td&gt;172.18.0.9&lt;/td&gt;
&lt;td&gt;192.168.99.100:8000-8001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kong Database&lt;/td&gt;
&lt;td&gt;172.18.0.3&lt;/td&gt;
&lt;td&gt;192.168.99.100:5432&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx Web Server&lt;/td&gt;
&lt;td&gt;172.18.0.6&lt;/td&gt;
&lt;td&gt;Not accessible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Server&lt;/td&gt;
&lt;td&gt;172.18.0.2&lt;/td&gt;
&lt;td&gt;Not accessible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTPBin&lt;/td&gt;
&lt;td&gt;172.18.0.5&lt;/td&gt;
&lt;td&gt;Not accessible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application Server A&lt;/td&gt;
&lt;td&gt;172.18.0.7&lt;/td&gt;
&lt;td&gt;Not accessible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application Server B&lt;/td&gt;
&lt;td&gt;172.18.0.4&lt;/td&gt;
&lt;td&gt;Not accessible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Kong Gateway APIs
&lt;/h2&gt;

&lt;p&gt;Kong can be administered with an internal RESTful API called the &lt;strong&gt;Admin API&lt;/strong&gt;. This API provides full control over the kong gateway.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;8001&lt;/code&gt; is the default port for HTTP traffic to the Admin API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;8444&lt;/code&gt; is the default port for HTTPS traffic to the Admin API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apart from the Admin API, we have another API using which the API consumers can consume the services. This API needs to be exposed to the world in order for the services to be accessible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;8000&lt;/code&gt; is the default port for HTTP traffic from Consumers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;8443&lt;/code&gt; is the default port for HTTPS traffic from Consumers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The traffic from consumers on 8000/8443 is forwarded to the corresponding service based upon the defined routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing the Admin GUI (Konga)
&lt;/h2&gt;

&lt;p&gt;The admin GUI can be found at 192.168.99.100:1337. Once you register yourself as an admin you'll be prompted to specify the kong admin URL using which the Admin GUI can create objects like services/routes etc on the kong node. &lt;/p&gt;

&lt;p&gt;The Kong Admin API is found at 192.168.99.100:8001. Once the Admin GUI is connected to the Admin API you’ll be directed to the main dashboard -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uAEV1Fnk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/kong-admin-gui-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uAEV1Fnk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/kong-admin-gui-dashboard.png" alt="Kong Admin GUI Dashboard" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Interacting with the Kong Admin API
&lt;/h2&gt;

&lt;p&gt;I’ve created a &lt;a href="https://github.com/adisakshya/api-gateway/blob/master/single-node-kong-gateway/postman-collection/api-gateway.postman_collection"&gt;Postman collection&lt;/a&gt; containing the requests that can be made to the Kong APIs for administrating and accessing services from the gateway. &lt;/p&gt;

&lt;p&gt;The Admin API provides a ‘/status’ route that can be used to check the status of a Kong node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WcLFPnfk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/kong-admin-api-status-check.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WcLFPnfk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/kong-admin-api-status-check.png" alt="Response from Kong Admin API - Status Route" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Admin API provides you with complete control over the Kong Node so there’s a lot you can do with this API like attaching/detaching backend services, creating/removing consumers, creating passwords for users and much more. &lt;/p&gt;

&lt;p&gt;To get the full list of routes provided by the Admin API you can call the ‘/endpoint’ route. I’ve included a few requests in the postman collection for you to explore the Admin API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering the microservices with the API gateway
&lt;/h2&gt;

&lt;p&gt;Having played with the Admin API now let’s attach our already running backend microservices with the API gateway. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every microservice will have a service object corresponding to it on the API gateway that is identified by the service name, hostname (or IP address) and a port number.&lt;/li&gt;
&lt;li&gt;Every service will have defined route(s) which can be called by the end-users.&lt;/li&gt;
&lt;li&gt;The request to any route is proxied to the corresponding backend microservice and the generated response is returned back to the end-user by the API gateway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create a service object for a backend microservice, we can send a POST request to the Admin API on the ‘/services’ route with the service name, hostname and port number (default 80).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WpEYsTTD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/create-app-server-a-service.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WpEYsTTD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/create-app-server-a-service.png" alt="Creating service object for Application Server A" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having created a service object for all our microservices we can get a list of available services on the API gateway using the Admin API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eyWptN4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/list-of-all-services.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eyWptN4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/list-of-all-services.png" alt="Listing all defined service objects using Kong Admin API" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having defined the service objects now we need to create the routes to which the API consumers can forward their requests. To create a route object for a gateway service, we can send a POST request to the Admin API on the ‘/routes’ route with the route name, route path and list of allowed methods on that path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Ar4GrcB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/create-route-for-httpbin-service.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Ar4GrcB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/create-route-for-httpbin-service.png" alt="Create a route for HTTPBin Service with GET, POST, and DELETE methods" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having created route objects for all our gateway services we can get a list of all available routes using the Admin API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--okqce-K_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/list-of-routes-kong-admin-api.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--okqce-K_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/list-of-routes-kong-admin-api.png" alt="Listing all defined route objects using Kong Admin API" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilizing the microservices from the API gateway
&lt;/h2&gt;

&lt;p&gt;All the routes defined on the gateway are reachable from port 8000. Let’s hit the routes we’ve created and analyze the response. We’re running a raw Nginx Web Server behind the route ‘/nginx’, let’s hit it first using the API -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jQC73Ogo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/nginx-service-get-route-no-auth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jQC73Ogo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/nginx-service-get-route-no-auth.png" alt="Hitting the GET /nginx route" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As expected the response returned is the default Nginx welcome page, this means that the backend Nginx web service running on 172.18.0.6 (inside docker network) is now accessible on the host via the API gateway. Now let’s try hitting the HTTPBin service -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BpIgYYlA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/httpbin-service-post-route-no-auth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BpIgYYlA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/setting-up-an-api-gateway-for-your-microservices/httpbin-service-post-route-no-auth.png" alt="Hitting the POST /httbin/post route" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The purpose of using the HTTPBin service was to analyze the returned response in detail. Focus on the fields named ‘X-Forwarded-Host’, ‘X-Forwarded-Path’ and ‘X-Forwarded-Prefix’, these fields are often referred to as &lt;a href="https://en.wikipedia.org/wiki/X-Forwarded-For"&gt;X-Forwarded-For&lt;/a&gt; (XFF) HTTP header fields.&lt;/p&gt;

&lt;p&gt;XFF fields are used for identifying the originating IP address of the consumer who is connecting to the service. There can be more details like UserId and Username of the consumer as part of XFF which can be utilized by the backend microservices. We’ll be exploring them in detail in upcoming articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;As you might have noticed already that all the services that we’ve created are open to access for anyone on the host. In the next article, we’ll understand some key concepts about authentication and authorization to secure our backend services. We’ll see how we can restrict access to a particular backend service on the user level.&lt;/p&gt;

&lt;p&gt;See you there!&lt;/p&gt;

&lt;h2&gt;
  
  
  Contribution
&lt;/h2&gt;

&lt;p&gt;Enjoyed the article, found it useful or learned something new? Consider sharing it so more people can benefit from it! Also, feel free to @ me on &lt;a href="https://twitter.com/adisakshya"&gt;Twitter&lt;/a&gt; with your opinions.&lt;/p&gt;

&lt;p&gt;Found a mistake? Feel free to mention it in comments or at &lt;a href="https://github.com/adisakshya/weblog/issues"&gt;GitHub issues&lt;/a&gt; or fix it using a &lt;a href="https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests"&gt;PR&lt;/a&gt; at &lt;a href="https://github.com/adisakshya/weblog"&gt;adisakshya/weblog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>apigateway</category>
      <category>api</category>
      <category>kong</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Introduction to API Gateway</title>
      <dc:creator>Adisakshya Chauhan</dc:creator>
      <pubDate>Tue, 01 Feb 2022 18:55:45 +0000</pubDate>
      <link>https://forem.com/adisakshya/introduction-to-api-gateway-1fpo</link>
      <guid>https://forem.com/adisakshya/introduction-to-api-gateway-1fpo</guid>
      <description>&lt;p&gt;Let’s say we are building an application that uses the microservices architecture. These types of applications provide API delivering a specific use case. Additionally, the number of running service instances or the number of services in an application itself can change, which should be hidden from clients. &lt;/p&gt;

&lt;p&gt;So how do the clients of Microservices-based applications access the individual services? The client application would need to interact with multiple different services to get the required information for the user and any update in a service should be hidden from clients.&lt;/p&gt;

&lt;p&gt;This is where the concept of API Gateway comes into the picture. It follows the literal meaning of a &lt;strong&gt;“Gateway” - the place which you must go through to get to somewhere else&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The API Gateway acts as a single entry point to the available backend services in an application for all the clients. It can also implement security, e.g. verify that the client is authorized to perform the request. Just like a security guard standing in front of the gateway and verifying if you are allowed to go beyond the gateway.&lt;/p&gt;

&lt;p&gt;In this &lt;a href="http://adisakshya.codes/weblog"&gt;API Gateway 101&lt;/a&gt; series, we’ll understand what these gateways are all about and get our hands dirty by setting up an API Gateway in front of an application that uses the microservices architecture deployed on Kubernetes. But one step at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an API?
&lt;/h2&gt;

&lt;p&gt;Firstly we need to understand what exactly an API is? &lt;/p&gt;

&lt;p&gt;Application Programming Interface (API) provides a blueprint that specifies how application services in a software system should interact with each other. It acts as a bridge between different services and abstracts away the underlying complexity to present it as a well-defined product.&lt;/p&gt;




&lt;p&gt;Cool, now let’s get started and understand - &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is an API Gateway?&lt;/li&gt;
&lt;li&gt;What is the difference between API Gateway, Load Balancer and Reverse Proxy?&lt;/li&gt;
&lt;li&gt;What is Kong?&lt;/li&gt;
&lt;li&gt;What is the use of an API Gateway in a microservices environment?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is an API Gateway?
&lt;/h2&gt;

&lt;p&gt;At the most basic level, an API follows a request-response cycle i.e., the API service accepts a remote request and returns a response. But this isn’t that simple, let’s think about various concerns when we host large-scale API -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want to protect our API services from overuse and abuse, so we should use an authentication service and rate-limiting etc.&lt;/li&gt;
&lt;li&gt;We want to understand how people use our APIs, so we’d like added analytics and monitoring tools.&lt;/li&gt;
&lt;li&gt;Over time we’ll add some new API services and retire others, but our clients will still want to find all our services in the same place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge is to provide the clients with a simple &amp;amp; dependable interface for enabling them to interact with our system and manage remote requests in a centralized manner.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An API gateway is an API management tool that sits in front of the backend API services and acts as a single entry point for all clients wanting to consume the services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The gateway intercepts all the incoming remote requests and can apply a variety of necessary checks and functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0EsrUL_r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/api-gateway.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0EsrUL_r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/api-gateway.png" alt="API Gateway Architecture" width="878" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  API Gateway vs Load Balancer vs Reverse Proxy
&lt;/h3&gt;

&lt;p&gt;At this point, an API gateway might sound pretty similar to a load balancer and reverse proxy. These applications sit between the clients and backend servers, accepting remote requests from the former and delivering responses from the latter. &lt;/p&gt;

&lt;p&gt;To understand the difference, let’s explore when and why they’re typically used -&lt;/p&gt;

&lt;h4&gt;
  
  
  Load Balancer
&lt;/h4&gt;

&lt;p&gt;Load balancing helps prevent the overloading of individual systems &amp;amp; prevent failure due to the same. A load balancer offers the ability to distribute incoming remote requests across multiple backend services. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RiQZanKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/loadbalancer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RiQZanKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/loadbalancer.png" alt="Load Balancer Architecture" width="756" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Say, we are running 5 instances of a backend service behind the load balancer. The incoming requests will be distributed between these 5 servers following the load balancing strategy like round-robin, least utilized etc. If any of these 5 servers fail due to any reason then the load balancer would distribute the incoming requests to the remaining operational backend services.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reverse Proxy
&lt;/h4&gt;

&lt;p&gt;A Reverse Proxy acts as a mediator between the client and one or more backend services.&lt;/p&gt;

&lt;p&gt;It can rewrite the URLs, so the client will not know who is sitting behind the reverse proxy. Its responsibility is to forward the remote request to the appropriate backend service that can fulfil it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w8eCn8Qi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/reverse-proxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w8eCn8Qi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/reverse-proxy.png" alt="Reverse Proxy Architecture" width="768" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A typical reverse proxy can be used for -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load Balancing&lt;/strong&gt;: it offers the ability to distribute incoming requests across multiple backend servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: content is often kept in a place called proxy cache. So for recurring requests, it can answer autonomously in less time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Encryption&lt;/strong&gt;: it can be configured to decrypt all incoming requests and encrypt all outgoing responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  API Gateway
&lt;/h4&gt;

&lt;p&gt;We can think of the API Gateway as a superset of a Reverse Proxy.&lt;/p&gt;

&lt;p&gt;The gateway hides the backend architecture from the clients. It addresses some common utilities like -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication and Authorization: It can control who can request what

&lt;ul&gt;
&lt;li&gt;Example - It offers the possibility to control the access to specific backend services based upon the plans that the API consumer have purchased.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;IP Whitelisting: It can grant the ability to use the API services only to specific IP addresses&lt;/li&gt;
&lt;li&gt;Rate Limiting, Throttling, and Quota: A limit can be set based on how many requests the backend servers can handle for a certain unit of time.

&lt;ul&gt;
&lt;li&gt;Looking at the commercial aspect, it offers the possibility to control the traffic that API consumers are using based on the plan they’ve purchased&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Logging, Tracing, Correlation: It can gather all the logs for each specific remote request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Differences at a glance -&lt;/strong&gt; &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Load Balancer&lt;/th&gt;
&lt;th&gt;Reverse Proxy&lt;/th&gt;
&lt;th&gt;API Gateway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Balance incoming requests&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL Rewrite&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prevention from Attack&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocol Translation&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Whitelisting&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate Limiting, Quota&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logging&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tracing&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health Checking&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic Config Updates&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What is Kong?
&lt;/h2&gt;

&lt;p&gt;Kong Gateway is one of the most popular open-source API gateways. It securely manages the communication between clients &amp;amp; backend services via an API.&lt;/p&gt;

&lt;p&gt;Kong sits in front of backend services and extends these APIs using &lt;a href="https://konghq.com/plugins"&gt;Plugins&lt;/a&gt; to provide extra utilities like authentication, rate-limiting etc&lt;/p&gt;

&lt;p&gt;A typical Kong API Gateway setup looks something like this -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VyUYuWH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/kong-api-gateway.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VyUYuWH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/adisakshya/weblog/static-assets/introduction-to-api-gateway/kong-api-gateway.png" alt="Kong API Gateway Architecture" width="880" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the architecture is deployed and running, every remote request to the backend services will hit kong first thus becoming the entry point for any API request.&lt;/p&gt;

&lt;p&gt;If you want to deep dive into working of Kong then you can refer to this - &lt;a href="https://konghq.com/faqs/"&gt;https://konghq.com/faqs/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the use of an API Gateway in a microservices environment?
&lt;/h2&gt;

&lt;p&gt;In a microservices architecture, each microservice exposes a set of endpoints with specific functionalities.&lt;/p&gt;

&lt;p&gt;Client applications usually want to consume functionality from more than one microservice. If there is a direct Client‑to‑Microservice Communication, then the client application would need to handle multiple calls to the backend microservice endpoints. When the client application is linked with the internal endpoints then evolving the microservices would mean updating the client application too. But ideally updating internal APIs should have minimal impact on the client application.&lt;/p&gt;

&lt;p&gt;This is where the API Gateway comes to the rescue, it encapsulates the backend services architecture and provides an API that can be customized for each client application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;We’ll get our hands dirty in the next article where we’ll &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spin several microservices&lt;/li&gt;
&lt;li&gt;Setup a Kong API Gateway in front of the microservices&lt;/li&gt;
&lt;li&gt;Access the created services them using the gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See you there!&lt;/p&gt;

&lt;h2&gt;
  
  
  Contribution
&lt;/h2&gt;

&lt;p&gt;Enjoyed the article, found it useful or learned something new? Consider sharing it so more people can benefit from it! Also, feel free to @ me on &lt;a href="http://twitter.com/adisakshya"&gt;Twitter&lt;/a&gt; with your opinions.&lt;/p&gt;

&lt;p&gt;Found a mistake? Feel free to mention it in comments or at &lt;a href="https://github.com/adisakshya/weblog"&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>apigateway</category>
      <category>microservices</category>
      <category>kong</category>
    </item>
    <item>
      <title>Extension Pack for VSCode &amp; Code Server</title>
      <dc:creator>Adisakshya Chauhan</dc:creator>
      <pubDate>Fri, 30 Jul 2021 18:54:31 +0000</pubDate>
      <link>https://forem.com/adisakshya/extension-pack-for-vscode-code-server-4o7h</link>
      <guid>https://forem.com/adisakshya/extension-pack-for-vscode-code-server-4o7h</guid>
      <description>&lt;p&gt;Hello DEVs, its my first post here!&lt;/p&gt;

&lt;p&gt;In this article, we'll create a common extension pack for VS Code and code-server, explore the involved compatibility issue and Travis Workspaces. Finally, we'll build a CI/CD pipeline to publish our extension pack to a GitHub release.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Extension packs in VS Code are useful when we want to bundle and install a set of extensions together. It also provides us with a way to share and organize our favourite extensions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;If you've never built an extension before, you'll need &lt;a href="https://code.visualstudio.com/download"&gt;VS Code&lt;/a&gt; and &lt;a href="https://nodejs.org/en/download/"&gt;NodeJS&lt;/a&gt; installed.&lt;/p&gt;

&lt;p&gt;We'll create a versioned extension pack, so a &lt;a href="https://docs.github.com/en/get-started/quickstart/create-a-repo"&gt;git repository&lt;/a&gt; is needed where we can maintain the source code of our extension pack. Also on this repository, a CI/CD pipeline will be built later in the article. The extension pack will be published on the repository release page by the pipeline, from where anyone can install and use it.&lt;/p&gt;

&lt;p&gt;The source code of the extension pack created for this article can be found at my GitHub repository - &lt;a href="https://github.com/adisakshya/extension-pack"&gt;adisakshya/extension-pack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once NodeJS is installed, the git repository is created and cloned we can move forward to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Yeoman &amp;amp; Code Generator
&lt;/h2&gt;

&lt;p&gt;To get started creating an extension pack,&lt;/p&gt;

&lt;p&gt;We'll need to install the &lt;a href="https://www.npmjs.com/package/yo"&gt;Yeoman&lt;/a&gt; scaffolding CLI tools as well as the &lt;a href="https://www.npmjs.com/package/generator-code"&gt;generator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yeoman can be installed using the following command -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g yo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can install the generator tools like so -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g generator-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating a New Extension Project
&lt;/h2&gt;

&lt;p&gt;With the correct tools installed, we are now ready to generate a new extension pack project. This is pretty easy as all of the scaffolding is already provided. So all we have to do is add to the list of extensions to be included.&lt;/p&gt;

&lt;p&gt;We can start this process by running the following command in our git project directory (extension-pack) -&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;




    


&lt;h3&gt;
  
  
  Understanding the generated code
&lt;/h3&gt;

&lt;p&gt;The following directory structure will be created inside the project directory by the generator -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-- .vscode
|   +-- launch.json
+-- .vscodeignore
+-- CHANGELOG.md
+-- package.json
+-- README.md
+-- vsc-extension-quickstart.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s explore everything in it -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vscode/launch.json&lt;/code&gt; is a file used to configure the debugger in VS Code.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.vscodeignore&lt;/code&gt; file excludes some files from being included in your extension's package.&lt;/li&gt;
&lt;li&gt;Record of all notable changes made to the project is maintained in &lt;code&gt;CHANGELOG.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;README.md&lt;/code&gt; is the project readme file, it'll also be used in the extension packs page in the VS Code marketplace.&lt;/li&gt;
&lt;li&gt;Every VS Code extension needs a manifest file &lt;code&gt;package.json&lt;/code&gt; at the root of the project directory structure. Most of the time we'll find ourselves tweaking the manifest file. A great reference can be found here at &lt;a href="https://code.visualstudio.com/api/references/extension-manifest"&gt;VS Code API Reference&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vsc-extension-quickstart.md&lt;/code&gt; feature some quick-start tips.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Make it yours
&lt;/h2&gt;

&lt;p&gt;Cool, so now that we understand (hopefully 😅) what everything means! We can move forward to update the &lt;code&gt;package.json&lt;/code&gt; manifest to customize our extension pack. The field names are quite clear with their meaning.&lt;/p&gt;

&lt;p&gt;First things first - Let’s name, describe and version our package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"adisakshya-extension-pack"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"displayName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"adisakshya-extension-pack"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Adisakshya's extension-pack for VS Code &amp;amp; Code Server"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second things second - Update the list of extensions in our pack.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;extensionPack&lt;/code&gt; field is a list of &lt;code&gt;extension-ids&lt;/code&gt;. The ID of the extension can be obtained from VS Code Marketplace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"extensionPack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"cssho.vscode-svgviewer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"docsmsft.docs-markdown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"eamodio.gitlens"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"EditorConfig.EditorConfig"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"esbenp.prettier-vscode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"formulahendry.code-runner"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"GitHub.github-vscode-theme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"GrapeCity.gc-excelviewer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"humao.rest-client"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"johnpapa.vscode-peacock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ms-azuretools.vscode-docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ms-python.python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ms-python.vscode-pylance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ms-vscode-remote.vscode-remote-extensionpack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ms-vscode.cpptools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ms-vscode.vscode-typescript-next"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ritwickdey.LiveServer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"VisualStudioExptTeam.vscodeintellicode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"vscode-icons-team.vscode-icons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"vsls-contrib.codetour"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third things third - Specify an icon&lt;/p&gt;

&lt;p&gt;We can specify an icon for our extension pack by placing our icon-file.png in our project directory. Its path has to be mentioned in the &lt;code&gt;package.json&lt;/code&gt; manifest like -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path/to/icon-file.png"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've specified what extensions need to be included and other associated information. We can pack the extensions in a file that can be used to install the extension pack on VS Code and code-server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packing the extensions
&lt;/h2&gt;

&lt;p&gt;Visual Studio Code Extensions (aka &lt;a href="https://www.npmjs.com/package/vsce"&gt;vsce&lt;/a&gt;) is a command-line tool for packing, publishing and managing VS Code extensions. It can be installed using the following command -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --global vsce
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll choose to pack our extension into a &lt;code&gt;VSIX&lt;/code&gt; file, using which people who wish to use the same pack can install it.&lt;/p&gt;

&lt;p&gt;We can package the extensions in a VSIX file using the following command in our project directory -&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;For users who receive the &lt;code&gt;VSIX&lt;/code&gt; file, they can install the extension pack on VS Code and code-server like so -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# VS Code
code --install-extension adisakshya-extension-pack-1.0.0.vsix

# Code Server
code-server --install-extension adisakshya-extension-pack-1.0.0.vsix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A handy NPM script can be included in &lt;code&gt;package.json&lt;/code&gt; -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vsce package"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be used as an &lt;code&gt;npm run pack&lt;/code&gt; or &lt;code&gt;yarn run pack&lt;/code&gt; in CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the VS Code compatibility
&lt;/h2&gt;

&lt;p&gt;This is one of the most important things we (or at least I) unconsciously ignored.&lt;/p&gt;

&lt;p&gt;I packed the extensions in a VSIX file and tried installing it on my local system with VS Code installed. Great, it worked as expected.&lt;/p&gt;

&lt;p&gt;I took the same VSIX file and tried installing it on a code-server setup in Windows Subsystem for Linux (WSL). And... saw the below message -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xHSboYgq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwwfa7yc0b6rablw1z38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xHSboYgq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwwfa7yc0b6rablw1z38.png" alt="VS Code Compatibility Error Message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay... so it said - &lt;strong&gt;Unable to install extension 'adisakshya-extension-pack' as it is not compatible with VS Code '1.53.2'&lt;/strong&gt;. I figured out that it had something to do with the version of VS Code for which the extension pack was created.&lt;/p&gt;

&lt;p&gt;One thing that looked SUS was the &lt;code&gt;engines.vscode&lt;/code&gt; field inside &lt;code&gt;package.json&lt;/code&gt;, which was the only thing I left as default. I went through the VS Code Extension API Reference to find out what this field really is and what I found was -&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When authoring an extension, you will need to describe what is the extension's compatibility to Visual Studio Code itself. — &lt;a href="https://code.visualstudio.com/api/working-with-extensions/publishing-extension#visual-studio-code-compatibility"&gt;API Reference&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;engines.vscode&lt;/code&gt; field is an object that contains a key matching the versions of VS Code that the extension or extension pack is compatible with. If I'm generating the scaffolding code on my system then this field is set to the version of VS Code I'm using by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vscode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.56.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was using the &lt;code&gt;1.56.0&lt;/code&gt; version of VS Code and generated the VSIX file with this configuration. &lt;code&gt;1.53.0&lt;/code&gt; was the version of VS Code associated with the code-server release that I was using.&lt;/p&gt;

&lt;p&gt;Okay, the extension pack that I created was meant to be compatible with version &lt;code&gt;1.56.0&lt;/code&gt; and above. While the code-server was using &lt;code&gt;1.53.0&lt;/code&gt; — this was the reason for that incompatibility message!&lt;/p&gt;

&lt;p&gt;I updated the &lt;code&gt;engines.vscode&lt;/code&gt; field to &lt;code&gt;^1.53.0&lt;/code&gt; to make it work, but still, the installation failed on code-server as some extensions inside the pack were not compatible with this version of VS Code. So next possible solution was to upgrade the code-server.&lt;/p&gt;

&lt;p&gt;I installed the &lt;code&gt;3.10.2&lt;/code&gt; version of code-server which used VS Code &lt;code&gt;1.56.0&lt;/code&gt; and tried installing my extension pack with &lt;code&gt;engines.vscode&lt;/code&gt; field set to &lt;code&gt;1.56.0&lt;/code&gt;. Great, it worked!&lt;/p&gt;

&lt;p&gt;So, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before packing our extension we must ensure that we’ve mentioned the &lt;code&gt;engines.vscode&lt;/code&gt; field correctly according to our use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To find out what version of VS Code engine is our code-server setup using, see the Welcome Screen -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kwtDeH2f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwbse20ih783i5rwj0y8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kwtDeH2f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwbse20ih783i5rwj0y8.png" alt="Code Server Welcome Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing the extension pack
&lt;/h2&gt;

&lt;p&gt;We'll be publishing the &lt;code&gt;VSIX&lt;/code&gt; file of our extension pack to a GitHub Release using Travis CI. Let’s define our expectations — The CI workflow should do the following -&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pack the extensions in a VSIX file&lt;/li&gt;
&lt;li&gt;Publish the VSIX file to GitHub release&lt;/li&gt;
&lt;li&gt;We should be able to download the latest release of the extension pack (using a single terminal command so that we don’t have to install it from the marketplace every time and can use this command in automation scripts)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While writing the Travis YML file I came across a much-awaited (at least for me) beta feature - &lt;a href="https://docs.travis-ci.com/user/using-workspaces"&gt;Workspaces&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Workspaces allow jobs within a build to share files. They are useful when you want to use build artefacts from a previous job. — Travis Workspaces Docs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cool, this means we can have two stages where the first stage would pack the extensions in the VSIX file and upload it to a workspace. Then the second stage would use the same workspace to get the VSIX file and publish it to a GitHub release.&lt;/p&gt;

&lt;p&gt;Now once released how we’ll use our extension pack’s VSIX file?&lt;/p&gt;

&lt;p&gt;VSIX files uploaded to a GitHub release can be downloaded using the &lt;code&gt;curl command&lt;/code&gt;. Then we can use the &lt;code&gt;code [or code-server] --install-extension&lt;/code&gt; command to install the extension pack. If you use an automation script to set up VS Code or code-server then this way you can write a single line in the script to install the extension pack as well.&lt;/p&gt;

&lt;p&gt;Cool, now we’ve defined what each stage in the pipeline should do -&lt;/p&gt;

&lt;p&gt;✅ What do we release?&lt;/p&gt;

&lt;p&gt;✅ How do we release?&lt;/p&gt;

&lt;p&gt;✅ How are we using, what we are releasing?&lt;/p&gt;

&lt;p&gt;So, let’s write the Travis CI configuration &lt;code&gt;.travis.yml&lt;/code&gt; -&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="c1"&gt;# Language and Node Engine&lt;/span&gt;
&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_js&lt;/span&gt;
&lt;span class="na"&gt;node_js&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;

&lt;span class="c1"&gt;# Environment Variables&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PACK_NAME=adisakshya-extension-pack&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;VERSION=$(node assets/version.js -p)&lt;/span&gt;

&lt;span class="c1"&gt;# Jobs&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# STAGE 1&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Build"&lt;/span&gt;
      &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Install VSCE&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm install --global vsce&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Pack extensions&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm run pack&lt;/span&gt;
      &lt;span class="c1"&gt;# Create a workspace&lt;/span&gt;
      &lt;span class="na"&gt;workspaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ws1&lt;/span&gt;
          &lt;span class="c1"&gt;# Upload artifact to the workspace&lt;/span&gt;
          &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${PACK_NAME}-${VERSION}.vsix&lt;/span&gt;
    &lt;span class="c1"&gt;# STAGE 2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitHub Release&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;VSIX&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Release"&lt;/span&gt;
      &lt;span class="c1"&gt;# Use an existing workspace&lt;/span&gt;
      &lt;span class="na"&gt;workspaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ws1&lt;/span&gt;
      &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;skip&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;skip&lt;/span&gt;
      &lt;span class="c1"&gt;# Publish VSIX file to GitHub release&lt;/span&gt;
      &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;releases&lt;/span&gt;
        &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${GITHUB_SECURE_TOKEN}&lt;/span&gt; &lt;span class="c1"&gt;# The GitHub Access Token&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${VERSION}&lt;/span&gt; &lt;span class="c1"&gt;# Name of release is set as the version of extension pack&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PACK_NAME}-${VERSION}.vsix&lt;/span&gt; &lt;span class="c1"&gt;# Attach VSIX file with the release&lt;/span&gt;
        &lt;span class="na"&gt;skip_cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's push all our changes to the GitHub repository of the project and check the &lt;a href="https://travis-ci.com/github/adisakshya/extension-pack/builds/226340367"&gt;build stages status&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ElQ416Sh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ztfo7wsnijrhb9hpeift.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ElQ416Sh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ztfo7wsnijrhb9hpeift.png" alt="CI Build Stages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On success, the VSIX file will be published to a &lt;a href="https://github.com/adisakshya/extension-pack/releases"&gt;GitHub release&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MRgHGqeT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzygs37jgvnw5jvfvzb2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MRgHGqeT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzygs37jgvnw5jvfvzb2.png" alt="GitHub Release Page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following command will download and install the VSIX file from the latest GitHub release -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://github.com/adisakshya/extension-pack/releases/latest/download/adisakshya-extension-pack.vsix &lt;span class="nt"&gt;-O&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
code &lt;span class="nt"&gt;--install-extension&lt;/span&gt; adisakshya-extension-pack.vsix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The format of the URL to download VSIX file from a specific GitHub release is -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://github.com/adisakshya/extension-pack/releases/download/&amp;lt;GIT_TAG&amp;gt;/adisakshya-extension-pack.vsix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Voila!
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Let’s use what we’ve created!&lt;/em&gt;&lt;/p&gt;


    


&lt;h2&gt;
  
  
  Contribution
&lt;/h2&gt;

&lt;p&gt;Enjoyed the article, found it useful or learned something new? Consider sharing it so more people can benefit from it! Also, feel free to @ me on &lt;a href="http://twitter.com/adisakshya"&gt;Twitter&lt;/a&gt; with your opinions.&lt;/p&gt;

&lt;p&gt;Found a mistake? Feel free to mention it in comments or at &lt;a href="https://github.com/adisakshya/weblog"&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>github</category>
      <category>productivity</category>
      <category>firstpost</category>
    </item>
  </channel>
</rss>
