<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Navendu Pottekkat</title>
    <description>The latest articles on DEV Community by Navendu Pottekkat (@pottekkat).</description>
    <link>https://dev.to/pottekkat</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%2F443466%2Fa397a1ba-38e5-4113-a32f-aae6fdfd0eda.png</url>
      <title>DEV Community: Navendu Pottekkat</title>
      <link>https://dev.to/pottekkat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pottekkat"/>
    <language>en</language>
    <item>
      <title>Building a Model Context Protocol (MCP) Server in Go</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Sun, 13 Apr 2025 06:28:52 +0000</pubDate>
      <link>https://dev.to/pottekkat/building-a-model-context-protocol-mcp-server-in-go-4314</link>
      <guid>https://dev.to/pottekkat/building-a-model-context-protocol-mcp-server-in-go-4314</guid>
      <description>&lt;p&gt;&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt; &lt;a href="https://modelcontextprotocol.io/quickstart/server" rel="noopener noreferrer"&gt;servers&lt;/a&gt; allow LLMs (MCP hosts/clients) to access prompts, resources, and tools in a standard way, allowing you to build agents and complex workflows on top of LLMs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/python-sdk" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; make building and integrating MCP clients and servers easy. While there isn't an official SDK for Go &lt;em&gt;&lt;a href="https://github.com/orgs/modelcontextprotocol/discussions/224" rel="noopener noreferrer"&gt;yet&lt;/a&gt;&lt;/em&gt;, the community-built &lt;a href="https://github.com/mark3labs/mcp-go" rel="noopener noreferrer"&gt;mark3labs/mcp-go&lt;/a&gt; has been gaining a lot of popularity among Go developers—including myself.&lt;/p&gt;

&lt;p&gt;I used this SDK today to make a real-world MCP for a real project, and it has been pretty neat so far. This article is a quick walkthrough of how &lt;a href="https://github.com/pottekkat/dicedb-mcp" rel="noopener noreferrer"&gt;I set up an MCP server&lt;/a&gt; using the MCP Go SDK for &lt;a href="https://dicedb.io/" rel="noopener noreferrer"&gt;DiceDB&lt;/a&gt;, an in-memory key-value store like Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the mcp-go Module
&lt;/h2&gt;

&lt;p&gt;To use the &lt;code&gt;mcp-go&lt;/code&gt; module, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/mark3labs/mcp-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also use the &lt;code&gt;dicedb-go&lt;/code&gt; module to talk to the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/dicedb/dicedb-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a New MCP Server
&lt;/h2&gt;

&lt;p&gt;The entire server fits into a single &lt;code&gt;main.go&lt;/code&gt; file, thanks to the abstractions provided by the SDK. Let's break down the important bits, starting with setting up the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;// Import ALL required modules&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"net"&lt;/span&gt;
    &lt;span class="s"&gt;"strconv"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/dicedb/dicedb-go"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/dicedb/dicedb-go/wire"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/mark3labs/mcp-go/mcp"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/mark3labs/mcp-go/server"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create a new MCP server&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewMCPServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"DiceDB MCP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// Name of the server&lt;/span&gt;
        &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// Version&lt;/span&gt;
        &lt;span class="c"&gt;// Set listChanged to false as this example&lt;/span&gt;
        &lt;span class="c"&gt;// server does not emit notifications&lt;/span&gt;
        &lt;span class="c"&gt;// when the list of available tool changes&lt;/span&gt;
        &lt;span class="c"&gt;// https://modelcontextprotocol.io/specification/2024-11-05/server/tools#capabilities&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithToolCapabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what's happening here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4: We import the MCP and DiceDB modules and some helpers.&lt;/li&gt;
&lt;li&gt;19-27: We create a new MCP server instance:

&lt;ul&gt;
&lt;li&gt;19: &lt;code&gt;"DiceDB MCP"&lt;/code&gt; is just a human-readable name.&lt;/li&gt;
&lt;li&gt;20: &lt;code&gt;"0.1.0"&lt;/code&gt; is the version.&lt;/li&gt;
&lt;li&gt;26: &lt;code&gt;server.WithToolCapabilities(false)&lt;/code&gt; indicates that the server won't notify clients when the list of available tools changes, which is the case for our simple server.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Define a New Tool
&lt;/h2&gt;

&lt;p&gt;Now, let's add a tool. This one just pings the DiceDB server to check if it's reachable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;pingTool&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ping"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// Tool name&lt;/span&gt;
        &lt;span class="c"&gt;// Short description of what the tool does&lt;/span&gt;
        &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ping the DiceDB server to check connectivity"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c"&gt;// Add a string property to the tool schema&lt;/span&gt;
        &lt;span class="c"&gt;// to accept the URL of the DiceDB server&lt;/span&gt;
        &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c"&gt;// Description of the property&lt;/span&gt;
            &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The URL of the DiceDB server in format 'host:port'"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c"&gt;// Default value that compliant clients&lt;/span&gt;
            &lt;span class="c"&gt;// can use if the value isn't explicitly set&lt;/span&gt;
            &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localhost:7379"&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;



&lt;p&gt;Under the hood, these definitions get translated to a JSON schema following the &lt;a href="https://www.jsonrpc.org/specification" rel="noopener noreferrer"&gt;JSON-RPC 2.0 specification&lt;/a&gt;. MCP clients use this schema to discover and call the tool.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;"ping"&lt;/code&gt; tool has the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;29: &lt;code&gt;mcp.NewTool("ping", ...&lt;/code&gt; defines a tool named &lt;code&gt;"ping"&lt;/code&gt; that can be invoked by MCP clients.&lt;/li&gt;
&lt;li&gt;31: &lt;code&gt;mcp.WithDescription&lt;/code&gt; adds a description property to the tool schema that helps both humans and AI (MCP host/client) decide which tool to use.&lt;/li&gt;
&lt;li&gt;34: &lt;code&gt;mcp.WithString&lt;/code&gt; adds a string property to the tool schema to obtain the URL of the DiceDB server (from the MCP host/client).&lt;/li&gt;
&lt;li&gt;39: &lt;code&gt;mcp.DefaultString&lt;/code&gt; isn't defined in the MCP specification, but compliant clients can use this to set a default value if none is provided.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we wire in the actual logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Handler Function
&lt;/h2&gt;

&lt;p&gt;Now that we've defined the tool let's add what happens when it's invoked. In our case, we want to ping the DiceDB server to check if it's reachable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pingTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallToolRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallToolResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Extract the URL argument from the client request&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Parse host and port from URL&lt;/span&gt;
        &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;7379&lt;/span&gt; &lt;span class="c"&gt;// Default to 7379 if no port is provided&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Atoi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Create a new DiceDB client&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dicedb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewToolResultText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error connecting to DiceDB: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Send the PING command to DiceDB&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;wire&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"PING"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="c"&gt;// Return the result to the MCP client&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewToolResultText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response from DiceDB: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we define a handler function that runs when the MCP client calls the tool. This is what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;45: Extracts the URL from the client request.&lt;/li&gt;
&lt;li&gt;48-56: Splits the string to separate host and port. If a port isn't specified, we fall back to DiceDB's default (&lt;code&gt;7379&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;59-62: Initializes a new DiceDB client using the &lt;code&gt;dicedb-go&lt;/code&gt; SDK.&lt;/li&gt;
&lt;li&gt;65: Sends a &lt;code&gt;PING&lt;/code&gt; command to DiceDB.&lt;/li&gt;
&lt;li&gt;68: Returns the formatted response back to the MCP client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See how the SDK provides neat wrappers like &lt;code&gt;NewToolResultText&lt;/code&gt; to structure responses as required by the MCP specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start the Server
&lt;/h2&gt;

&lt;p&gt;All that's left is to start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeStdio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error starting server: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;



&lt;p&gt;This uses the &lt;a href="https://modelcontextprotocol.io/docs/concepts/transports#standard-input%2Foutput-stdio" rel="noopener noreferrer"&gt;stdio transport&lt;/a&gt;, meaning our MCP server communicates using standard input and output streams. This is the simplest way to run an MCP server locally.&lt;/p&gt;

&lt;p&gt;The MCP specification also allows SSE or &lt;a href="https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse" rel="noopener noreferrer"&gt;Server-Sent Events transport&lt;/a&gt;, which enables server-to-client streaming and uses HTTP POST for client-to-server messages. You can also create &lt;a href="https://modelcontextprotocol.io/docs/concepts/transports#custom-transports" rel="noopener noreferrer"&gt;custom transports&lt;/a&gt; for specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build/Install the Server
&lt;/h2&gt;

&lt;p&gt;Before using the server, build the binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; ./dist/dicedb-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install it globally (adds it to &lt;code&gt;$GOBIN&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you either a local or a global runnable binary that can be executed by MCP clients and hosts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use with MCP Hosts/Clients
&lt;/h2&gt;

&lt;p&gt;To use this MCP server with host applications like Claude Desktop or Cursor, add it to their configuration file (&lt;code&gt;claude_desktop_config.json&lt;/code&gt; or &lt;code&gt;mcp.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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&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;"dicedb-mcp"&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;"command"&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/dicedb-mcp"&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can prompt the LLM like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you check if DiceDB is running at localhost:7379?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The LLM will detect the &lt;code&gt;ping&lt;/code&gt; tool we registered, ask to run it, and show the result returned by the MCP server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Make sure you have DiceDB running. You can also apply this pattern to wrap other tools into MCP servers instead of DiceDB, as used in this example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;We've built a minimal example to demonstrate how the MCP Go SDK works. It is functional but intentionally simple.&lt;/p&gt;

&lt;p&gt;You can check out a more &lt;a href="https://github.com/pottekkat/dicedb-mcp" rel="noopener noreferrer"&gt;robust DiceDB MCP implementation&lt;/a&gt; (with additional tools and features) on GitHub.&lt;/p&gt;

&lt;p&gt;To go further, I recommend these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/introduction" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;Core Archetecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mark3labs/mcp-go/tree/main/examples" rel="noopener noreferrer"&gt;MCP Go SDK Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>ai</category>
      <category>mcp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Kubernetes Gateway API v1.0: Should You Switch?</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Thu, 04 Jan 2024 02:20:45 +0000</pubDate>
      <link>https://dev.to/apisix/kubernetes-gateway-api-v10-should-you-switch-45c0</link>
      <guid>https://dev.to/apisix/kubernetes-gateway-api-v10-should-you-switch-45c0</guid>
      <description>&lt;p&gt;It has been over a month since the &lt;a href="https://kubernetes.io/blog/2023/10/31/gateway-api-ga/" rel="noopener noreferrer"&gt;Kubernetes Gateway API made its v1.0 release&lt;/a&gt;, signifying graduation to the generally available status for some of its key APIs.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://api7.ai/blog/gateway-vs-ingress-api" rel="noopener noreferrer"&gt;wrote about the Gateway API&lt;/a&gt; when it graduated to beta last year, but a year later, the question still remains. Should you switch to the Gateway API from the Ingress API?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://api7.ai/blog/gateway-vs-ingress-api" rel="noopener noreferrer"&gt;My answer from last year&lt;/a&gt; was you shouldn't. And I had &lt;em&gt;strong&lt;/em&gt; reasons.&lt;/p&gt;

&lt;p&gt;The Gateway API and its implementations were still in their infancy. The Ingress API, on the other hand, was stable and covered some primary use cases that might work for most users.&lt;/p&gt;

&lt;p&gt;For users requiring more capabilities, I suggested using the &lt;a href="https://dev.to/posts/extending-apisix-ingress/#custom-crds"&gt;custom resources&lt;/a&gt; provided by the Ingress controllers by trading off portability (switching between different Ingress implementations).&lt;/p&gt;

&lt;p&gt;With the v1.0 release, this might change. The Gateway API is much more capable now, and its &lt;a href="https://gateway-api.sigs.k8s.io/implementations/" rel="noopener noreferrer"&gt;20+ implementations&lt;/a&gt; are catching up quickly.&lt;/p&gt;

&lt;p&gt;So, if you are starting anew and choosing between the Ingress and the Gateway API, I suggest you pick the Gateway API if the API and the &lt;a href="https://apisix.apache.org/docs/ingress-controller/tutorials/configure-ingress-with-gateway-api/" rel="noopener noreferrer"&gt;implementation you choose&lt;/a&gt; support all the features you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Wrong with the Ingress API?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" rel="noopener noreferrer"&gt;Ingress API&lt;/a&gt; works very well, but only for a small subset of common use cases. To extend its capabilities, Ingress implementations started using custom annotations.&lt;/p&gt;

&lt;p&gt;For example, if you chose Nginx Ingress, you will use some of its &lt;a href="https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md" rel="noopener noreferrer"&gt;dozens of annotations&lt;/a&gt; that are not portable if you decide to switch to another Ingress implementation like &lt;a href="https://apisix.apache.org/docs/ingress-controller/concepts/annotations/" rel="noopener noreferrer"&gt;Apache APISIX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These implementation-specific annotations are also cumbersome to manage and defeat the purpose of managing Ingress in a Kubernetes-native way.&lt;/p&gt;

&lt;p&gt;Eventually, Ingress controller implementations started developing their own CRDs to expose more features to Kubernetes users. These CRDs are specific to the Ingress controller. But if you can sacrifice portability and stick to one Ingress controller, the CRDs are easier to work with and offer more features.&lt;/p&gt;

&lt;p&gt;The Gateway API aims to solve this problem once and for all by providing the vendor agnosticism of the Ingress API and the flexibility of the CRDs. It is positioned very well to achieve this goal.&lt;/p&gt;

&lt;p&gt;In the long term, the Ingress API is not expected to receive any new features, and all efforts will be made to converge with the Gateway API. So, adopting the Ingress API can cause issues when you inadvertently hit limits with its capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obvious Benefits
&lt;/h2&gt;

&lt;p&gt;Expressive, extensible, and role-oriented are the key ideas that shaped the development of the Gateway API.&lt;/p&gt;

&lt;p&gt;Unlike the Ingress API, the Gateway API is a collection of multiple APIs (HTTPRoute, Gateway, GatewayClass, etc.), each catering to different organizational roles.&lt;/p&gt;

&lt;p&gt;For example, the application developers need to only care about the HTTPRoute resource, where they can define rules to route traffic. They can delegate the cluster-level details to an operator who manages the cluster and ensures that it meets the developers' needs using the Gateway resource.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F12%2F08%2FwinePDii_gateway-api.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F12%2F08%2FwinePDii_gateway-api.png" alt="The Gateway API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://gateway-api.sigs.k8s.io/#why-does-a-role-oriented-api-matter" rel="noopener noreferrer"&gt;role-oriented design&lt;/a&gt; of the API allows different people to use the cluster while maintaining control.&lt;/p&gt;

&lt;p&gt;The Gateway API is also much more capable than the Ingress API. Features that require annotations in the Ingress API are supported out-of-the-box in the Gateway API.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Official Extension
&lt;/h2&gt;

&lt;p&gt;Although the Gateway API is an official Kubernetes API, it is implemented as a set of CRDs.&lt;/p&gt;

&lt;p&gt;This is no different from using default Kubernetes resources. But you just have to &lt;a href="https://gateway-api.sigs.k8s.io/guides/#installing-gateway-api" rel="noopener noreferrer"&gt;install these CRDs&lt;/a&gt; like an official extension.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F12%2F08%2FXllJX2YG_apisix-ingress.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F12%2F08%2FXllJX2YG_apisix-ingress.png" alt="APISIX's Gateway API Support"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows for fast iteration compared to Kubernetes, which is slowly moving toward long-term stability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Will It Proliferate?
&lt;/h2&gt;

&lt;p&gt;As &lt;a href="https://xkcd.com/927/" rel="noopener noreferrer"&gt;this famous XKCD comic&lt;/a&gt; reminds us frequently, standards tend to proliferate.&lt;/p&gt;

&lt;p&gt;A version of this was seen in the Ingress and Gateway APIs. It usually goes like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A standard emerges to unify different projects/their standards (Kubernetes Ingress API).&lt;/li&gt;
&lt;li&gt;The unified standard has limitations the implementors want to overcome (Ingress API was limited).&lt;/li&gt;
&lt;li&gt;Implementations diverge from the standard because of these limitations (Custom CRDs, annotations).&lt;/li&gt;
&lt;li&gt;Each implementation now has its own standard (non-portable CRDs, annotations).&lt;/li&gt;
&lt;li&gt;A new standard emerges to unify these different standards (Kubernetes Gateway API).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is reasonable to think that the Gateway API might not be the end game here. But I believe it has every chance of being the standard for routing in Kubernetes.&lt;/p&gt;

&lt;p&gt;Again, I have my &lt;em&gt;strong&lt;/em&gt; reasons.&lt;/p&gt;

&lt;p&gt;Broad adoption is critical to prevent standard proliferation as there are fewer incentives for the implementations to work on a different standard. The Gateway API already has more than 25 implementations.&lt;/p&gt;

&lt;p&gt;An implementation can conform to the Gateway API on different levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Core&lt;/strong&gt;: All implementations are expected to conform to these.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended&lt;/strong&gt;: These might only be available in some implementations but are standard APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation-specific&lt;/strong&gt;: Specific to implementations but added through standard extension points.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A niche feature can move from implementation-specific to extended to core as more implementations support these features. i.e., the API allows room for custom extensions while ensuring it follows the standard.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://smi-spec.io/" rel="noopener noreferrer"&gt;Service Mesh Interface (SMI)&lt;/a&gt; project was a similar attempt to standardize configuring service meshes in Kubernetes. However, the project received little traction after the initial involvement of the service mesh projects and slowly died out.&lt;/p&gt;

&lt;p&gt;SMI did not support many common denominator features that users expected in a service mesh. It also did not move fast enough to support these features. Eventually, service mesh implementations fell behind in conforming to SMI (I used to work closely with SMI under the &lt;a href="https://github.com/cncf/tag-network" rel="noopener noreferrer"&gt;CNCF TAG Network&lt;/a&gt; on a project that reported SMI conformance).&lt;/p&gt;

&lt;p&gt;These are universal reasons, but the project is now being resurrected through the Gateway API. The &lt;a href="https://gateway-api.sigs.k8s.io/#gateway-api-for-service-mesh-the-gamma-initiative" rel="noopener noreferrer"&gt;Gateway API for Mesh Management and Administration (GAMMA) initiative&lt;/a&gt; aims to extend the Gateway API to work with service meshes.&lt;/p&gt;

&lt;p&gt;The SMI project &lt;a href="https://smi-spec.io/blog/announcing-smi-gateway-api-gamma/" rel="noopener noreferrer"&gt;recently merged with the GAMMA initiative&lt;/a&gt;, which is excellent for the Gateway API. Istio, undoubtedly the most popular service mesh, also &lt;a href="https://istio.io/latest/blog/2022/gateway-api-beta/" rel="noopener noreferrer"&gt;announced&lt;/a&gt; that the Gateway API will be the default API to manage Istio in the future. Such adoptions prevent proliferation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Guide
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://gateway-api.sigs.k8s.io/guides/migrating-from-ingress/" rel="noopener noreferrer"&gt;Gateway API documentation&lt;/a&gt; has a comprehensive guide on migrating your Ingress resources to Gateway resources. Instead of restating it, let's try using the &lt;a href="https://github.com/kubernetes-sigs/ingress2gateway" rel="noopener noreferrer"&gt;ingress2gateway&lt;/a&gt; tool to convert our Ingress resources to corresponding Gateway API resources.&lt;/p&gt;

&lt;p&gt;You can download and install the binary for your operating system directly from the &lt;a href="https://github.com/kubernetes-sigs/ingress2gateway/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;releases page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's take a simple Ingress resource:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;httpbin-route&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;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apisix&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local.httpbin.org&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&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="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&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;httpbin&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will route all traffic with the provided host address to the &lt;code&gt;httpbin&lt;/code&gt; service.&lt;/p&gt;

&lt;p&gt;To convert it to the Gateway API resource, we can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ingress2gateway print &lt;span class="nt"&gt;--input_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Gateway API resource will be as shown below:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway.networking.k8s.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;httpbin-route&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;hostnames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;local.httpbin.org&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PathPrefix&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
    &lt;span class="na"&gt;backendRefs&lt;/span&gt;&lt;span class="pi"&gt;:&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;httpbin&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Viable Alternatives
&lt;/h2&gt;

&lt;p&gt;There are other viable alternatives for configuring gateways in Kubernetes.&lt;/p&gt;

&lt;p&gt;In Apache APISIX, you can deploy it in &lt;a href="https://apisix.apache.org/docs/apisix/next/deployment-modes/#standalone" rel="noopener noreferrer"&gt;standalone mode&lt;/a&gt; and define route configurations in a yaml file. You can update this yaml file through traditional workflows, and it can be pretty helpful in scenarios where managing the gateway configuration via the Kubernetes API is not required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apisix.apache.org/docs/ingress-controller/tutorials/proxy-the-httpbin-service/" rel="noopener noreferrer"&gt;Implementation-specific custom CRDs&lt;/a&gt; are also viable alternatives if you don't plan to switch to a different solution or if your configuration is small enough to migrate easily.&lt;/p&gt;

&lt;p&gt;In any case, the Gateway API is here to stay.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cloud</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A "Tiny" APISIX Plugin</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Mon, 27 Nov 2023 16:00:56 +0000</pubDate>
      <link>https://dev.to/apisix/a-tiny-apisix-plugin-7a1</link>
      <guid>https://dev.to/apisix/a-tiny-apisix-plugin-7a1</guid>
      <description>&lt;p&gt;A key feature of Apache APISIX is its pluggable architecture. In addition to providing &lt;a href="https://apisix.apache.org/plugins/" rel="noopener noreferrer"&gt;80+ Lua plugins&lt;/a&gt; out of the box, APISIX also supports external plugins written in other languages through &lt;a href="https://apisix.apache.org/docs/go-plugin-runner/getting-started/" rel="noopener noreferrer"&gt;plugin runners&lt;/a&gt; and &lt;a href="https://apisix.apache.org/docs/apisix/wasm/" rel="noopener noreferrer"&gt;WebAssembly (Wasm)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we will write a "tiny" Go plugin for APISIX, compile it to a Wasm binary, run it in APISIX, and learn how it all works. We will also compare the benefits and costs of using Wasm plugins, external plugins (plugin runners), and native Lua plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  APISIX and Wasm
&lt;/h2&gt;

&lt;p&gt;APISIX supports Wasm through the &lt;a href="https://github.com/proxy-wasm/spec" rel="noopener noreferrer"&gt;WebAssembly for Proxies (proxy-wasm) specification&lt;/a&gt;. APISIX is a host environment that implements the specification, and developers can use the &lt;a href="https://github.com/proxy-wasm/spec#sdks" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; available in multiple languages to create plugins.&lt;/p&gt;

&lt;p&gt;Using Wasm plugins in APISIX has multiple advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many programming languages compile to Wasm. This allows you to leverage the capabilities of your tech stack in APISIX plugins.&lt;/li&gt;
&lt;li&gt;The plugins run inside APISIX and not on external plugin runners. This means you compromise less on performance while writing external plugins.&lt;/li&gt;
&lt;li&gt;Wasm plugins run inside APISIX but in a separate VM. So even if the plugin crashes, APISIX can continue to run.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;APISIX can only maintain its Wasm support without having to maintain plugin runners for multiple languages*.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;* These advantages come with a set of caveats which we will look at later.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;APISIX's plugin architecture below shows native Lua plugins, external plugins through plugin runners, and Wasm plugins:&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F27%2FyBnZnCrv_plugin-route-light.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F27%2FyBnZnCrv_plugin-route-light.png" alt="Lua plugins, plugin runners, and Wasm plugins"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A "Tiny" Go Plugin
&lt;/h2&gt;

&lt;p&gt;Let's get coding! To write a Go plugin, we will use the &lt;a href="https://github.com/tetratelabs/proxy-wasm-go-sdk" rel="noopener noreferrer"&gt;proxy-wasm-go-sdk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Reading through the documentation, you will understand why this plugin is called "tiny," i.e., the SDK uses the &lt;a href="https://tinygo.org/" rel="noopener noreferrer"&gt;TinyGo&lt;/a&gt; compiler instead of the official Go compiler. You can read more about why this is the case on the &lt;a href="https://github.com/tetratelabs/proxy-wasm-go-sdk/blob/main/doc/OVERVIEW.md" rel="noopener noreferrer"&gt;SDK\'s overview page&lt;/a&gt;, but the TLDR version is that the Go compiler can only produce Wasm binaries that run in the browser.&lt;/p&gt;

&lt;p&gt;For our example, we will create a plugin that adds a response header. The code below is pretty self-explanatory, and you can refer to &lt;a href="https://github.com/apache/apisix/blob/master/t/wasm/" rel="noopener noreferrer"&gt;other plugins&lt;/a&gt; for more implementation details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// references:&lt;/span&gt;
&lt;span class="c"&gt;// https://github.com/tetratelabs/proxy-wasm-go-sdk/tree/main/examples&lt;/span&gt;
&lt;span class="c"&gt;// https://github.com/apache/apisix/blob/master/t/wasm/&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/valyala/fastjson"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;proxywasm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetVMContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;vmContext&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// each plugin has its own VMContext.&lt;/span&gt;
&lt;span class="c"&gt;// it is responsible for creating multiple PluginContexts for each route.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;vmContext&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultVMContext&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// each route has its own PluginContext.&lt;/span&gt;
&lt;span class="c"&gt;// it corresponds to one instance of the plugin.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;vmContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;NewPluginContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contextID&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PluginContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pluginContext&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;pluginContext&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultPluginContext&lt;/span&gt;
    &lt;span class="n"&gt;Headers&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pluginContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OnPluginStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pluginConfigurationSize&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPluginStartStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;proxywasm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetPluginConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;proxywasm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogErrorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error reading plugin configuration: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPluginStartStatusFailed&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;fastjson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parser&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;proxywasm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogErrorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error decoding plugin configuration: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPluginStartStatusFailed&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hdr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStringBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStringBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnPluginStartStatusOK&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// each HTTP request to a route has its own HTTPContext&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pluginContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;NewHttpContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contextID&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;httpContext&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultHttpContext&lt;/span&gt;
    &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pluginContext&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;httpContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OnHttpResponseHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numHeaders&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endOfStream&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hdr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;proxywasm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReplaceHttpResponseHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hdr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ActionContinue&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To compile our plugin to a Wasm binary, we can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tinygo build &lt;span class="nt"&gt;-o&lt;/span&gt; custom_response_header.go.wasm &lt;span class="nt"&gt;-scheduler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasi ./main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring APISIX to Run the Plugin
&lt;/h2&gt;

&lt;p&gt;To use the Wasm plugin, we first have to update our APISIX configuration file to add this:&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;wasm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&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;custom-response-header&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7000&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;/opt/apisix/wasm/custom_response_header.go.wasm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can create a route and enable this plugin:&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;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/*&lt;/span&gt;
    &lt;span class="na"&gt;upstream&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roundrobin&lt;/span&gt;
      &lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:80"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;custom-response-header&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;conf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;{&lt;/span&gt;
          &lt;span class="s"&gt;"headers": [{&lt;/span&gt;
            &lt;span class="s"&gt;"name": "X-Go-Wasm", "value": "APISIX"&lt;/span&gt;
          &lt;span class="s"&gt;}]&lt;/span&gt;
        &lt;span class="s"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;#END&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The above configuration assumes that APISIX is deployed in &lt;a href="https://apisix.apache.org/docs/apisix/deployment-modes/#standalone" rel="noopener noreferrer"&gt;standalone mode&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Plugin
&lt;/h2&gt;

&lt;p&gt;We can send a request to the created route to test the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:9080 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response header would be as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
X-Go-Wasm: APISIX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wasm for the Win?
&lt;/h2&gt;

&lt;p&gt;From this article, it seems like using Wasm plugins benefits both users and APISIX maintainers.&lt;/p&gt;

&lt;p&gt;A test using our example &lt;code&gt;custom-response-header&lt;/code&gt; function implemented through a Lua plugin, an external Go plugin runner, and a Wasm plugin show how the performance varies:&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F27%2FbbkiAJgI_plugin-performance-light.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F27%2FbbkiAJgI_plugin-performance-light.png" alt="Wasm plugins aren't that bad!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Running 30s tests with 5 threads and 50 connections using &lt;a href="https://github.com/wg/wrk" rel="noopener noreferrer"&gt;wrk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Looking solely at this example, it might be tempting to ask why anyone would want to use plugin runners or write Lua plugins. Well, all the advantages of using Wasm comes with the following caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited plugins&lt;/strong&gt;: The Wasm implementation of programming languages often lacks complete support. For Go, we were limited to using the TinyGo compiler, similar to other languages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immature stack&lt;/strong&gt;: Wasm and its usage outside the browser is still a relatively new concept. The proxy-wasm spec also has its limitations due to its relative novelty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of concurrency&lt;/strong&gt;: Wasm does not have built-in concurrency support. This could be a deal breaker for typical APISIX uses cases where high performance is critical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better alternatives&lt;/strong&gt;: Since APISIX can be extended using Lua plugins or plugin runners, there are always alternatives to using Wasm.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite these caveats, the future of Wasm in APISIX and other proxies seems promising. You can choose to hop on the Wasm bandwagon if its benefits tip the scale against these costs. But currently, APISIX plans to continue supporting all three ways of creating custom plugins for the foreseeable future.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>webassembly</category>
      <category>go</category>
    </item>
    <item>
      <title>How is Apache APISIX Fast?</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Wed, 13 Sep 2023 07:01:22 +0000</pubDate>
      <link>https://dev.to/apisix/how-is-apache-apisix-fast-283f</link>
      <guid>https://dev.to/apisix/how-is-apache-apisix-fast-283f</guid>
      <description>&lt;p&gt;"High speed," "minimum latency," and "ultimate performance" are often used to characterize &lt;a href="https://api7.ai/apisix"&gt;Apache APISIX&lt;/a&gt;. Even when someone asks me about APISIX, my answer always includes "high-performance cloud native API gateway."&lt;/p&gt;

&lt;p&gt;Performance benchmarks (vs. &lt;a href="https://api7.ai/blog/apisix-kong-3-0-performance-comparison"&gt;Kong&lt;/a&gt;, &lt;a href="https://apisix.apache.org/blog/2021/06/10/apache-apisix-and-envoy-performance-comparison/"&gt;Envoy&lt;/a&gt;) confirm these characteristics are indeed accurate (&lt;a href="https://github.com/api7/apisix-benchmark"&gt;test yourself&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lo_5Igxw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/08/L0HjyWGM_apisix-vs-kong-light.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lo_5Igxw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/08/L0HjyWGM_apisix-vs-kong-light.png" alt="High speed, minimum latency, and ultimate performance" width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/api7/apisix-benchmark"&gt;Tests run&lt;/a&gt; for 10 rounds with 5000 unique routes on Standard D8s v3 (8 vCPUs, 32 GiB memory).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But how does APISIX achieve this?&lt;/p&gt;

&lt;p&gt;To answer that question, we must look at three things: etcd, hash tables, and radix trees.&lt;/p&gt;

&lt;p&gt;In this article, we will look under the hood of APISIX and see what these are and how all of these work together to keep APISIX maintaining peak performance while handling significant traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  etcd as the Configuration Center
&lt;/h2&gt;

&lt;p&gt;APISIX uses &lt;a href="https://etcd.io/"&gt;etcd&lt;/a&gt; to store and synchronize configurations.&lt;/p&gt;

&lt;p&gt;etcd is designed to work as a key-value store for configurations of large-scale distributed systems. APISIX is intended to be distributed and highly scalable from the ground up, and using etcd over traditional databases facilitates that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FWCXTIFO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/wjH9wJfU_architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FWCXTIFO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/wjH9wJfU_architecture.png" alt="APISIX architecture" width="800" height="628"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another key indispensable feature for API gateways is to be highly available, avoiding downtime and data loss. You can efficiently achieve this by deploying multiple instances of etcd to ensure a fault-tolerant, cloud native architecture.&lt;/p&gt;

&lt;p&gt;APISIX can read/write configurations from/to etcd with minimum latency. Changes to the configuration files are notified instantly, allowing APISIX to monitor only the etcd updates instead of polling a database frequently, which can add performance overhead.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://etcd.io/docs/v3.5/learning/why/#comparison-chart"&gt;chart&lt;/a&gt; summarizes how etcd compares with other databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash Tables for IP Addresses
&lt;/h2&gt;

&lt;p&gt;IP address-based allowlists/denylists are a common use case for API gateways.&lt;/p&gt;

&lt;p&gt;To achieve high performance, APISIX stores the list of IP addresses in a hash table and uses it for matching (O(1)) than iterating through the list (O(N)).&lt;/p&gt;

&lt;p&gt;As the number of IP addresses in the list increases, the performance impact of using hash tables for storage and matching becomes apparent.&lt;/p&gt;

&lt;p&gt;Under the hood, APISIX uses the &lt;a href="https://github.com/api7/lua-resty-ipmatcher"&gt;lua-resty-ipmatcher&lt;/a&gt; library to implement this functionality. The example below shows how the library is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ipmatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"resty.ipmatcher"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ipmatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s2"&gt;"162.168.46.72"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"17.172.224.47"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"216.58.32.170"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"17.172.224.47"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;-- true&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"176.24.76.126"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;-- false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library uses Lua tables which are hash tables. The IP addresses are hashed and stored as indices in a table, and to search for a given IP address, you just have to index the table and test whether it is nil or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--heQ91Gyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/5N5UFBWG_hash-table.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--heQ91Gyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/5N5UFBWG_hash-table.png" alt="Storing IP addresses in a hash table" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To search for an IP address, it first computes the hash (index) and checks its value. If it is non-empty, we have a match. This is done in constant time O(1).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Radix Trees for Routing
&lt;/h2&gt;

&lt;p&gt;Please forgive me for tricking you into a data structures lesson! But hear me out; this is where it gets interesting.&lt;/p&gt;

&lt;p&gt;A key area where APISIX optimizes performance is route matching.&lt;/p&gt;

&lt;p&gt;APISIX matches a route with a request from its URI, HTTP methods, host, and other information (see &lt;a href="https://github.com/apache/apisix/blob/98e56716fdf76b97c90531cac24de811d841c296/conf/config-default.yaml#L77"&gt;router&lt;/a&gt;). And this needs to be efficient.&lt;/p&gt;

&lt;p&gt;If you have read the previous section, an obvious answer would be to use a hash algorithm. But route matching is tricky because multiple requests can match the same route.&lt;/p&gt;

&lt;p&gt;For example, if we have a route &lt;code&gt;/api/*&lt;/code&gt;, then both &lt;code&gt;/api/create&lt;/code&gt; and &lt;code&gt;/api/destroy&lt;/code&gt; must match the route. But this is not possible with a hash algorithm.&lt;/p&gt;

&lt;p&gt;Regular expressions can be an alternate solution. Routes can be configured in a regex, and it can match multiple requests without the need to hardcode each request.&lt;/p&gt;

&lt;p&gt;If we take our previous example, we can use the regex &lt;code&gt;/api/[A-Za-z0-9]+&lt;/code&gt; to match both &lt;code&gt;/api/create&lt;/code&gt; and &lt;code&gt;/api/destroy&lt;/code&gt;. More complex regexes could match more complex routes.&lt;/p&gt;

&lt;p&gt;But regex is slow! And we know APISIX is fast. So instead, APISIX uses radix trees which are compressed prefix trees (trie) that work really well for fast lookups.&lt;/p&gt;

&lt;p&gt;Let's look at a simple example. Suppose we have the following words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;romane&lt;/li&gt;
&lt;li&gt;romanus&lt;/li&gt;
&lt;li&gt;romulus&lt;/li&gt;
&lt;li&gt;rubens&lt;/li&gt;
&lt;li&gt;ruber&lt;/li&gt;
&lt;li&gt;rubicon&lt;/li&gt;
&lt;li&gt;rubicundus&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A prefix tree would store it like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AHrULzXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/wDoV1tl6_prefix-tree.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AHrULzXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/wDoV1tl6_prefix-tree.png" alt="Prefix tree" width="800" height="1001"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The highlighted traversal shows the word "rubens."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A radix tree optimizes a prefix tree by merging child nodes if a node only has one child node. Our example trie would look like this as a radix tree:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--42OAahPV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/ePG0v1sB_radix-tree.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--42OAahPV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/ePG0v1sB_radix-tree.png" alt="Radix tree" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The highlighted traversal still shows the word "rubens." But the tree looks much smaller!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you &lt;a href="https://docs.api7.ai/apisix/getting-started/configure-routes"&gt;create routes in APISIX&lt;/a&gt;, APISIX stores them in these trees.&lt;/p&gt;

&lt;p&gt;APISIX can then work flawlessly because the time it takes to match a route only depends on the length of the URI in the request and is independent of the number of routes (O(K), K is the length of the key/URI).&lt;/p&gt;

&lt;p&gt;So APISIX will be as quick as it is when matching 10 routes when you first start out and 5000 routes when you scale.&lt;/p&gt;

&lt;p&gt;This crude example shows how APISIX can store and match routes using radix trees:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ULcVMM_8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/eiOwAjNu_apisix-route-matching.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ULcVMM_8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.apiseven.com/uploads/2023/06/12/eiOwAjNu_apisix-route-matching.png" alt="Crude example of route matching in APISIX" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The highlighted traversal shows the route &lt;code&gt;/user/*&lt;/code&gt; where the &lt;code&gt;*&lt;/code&gt; represents a prefix. So a URI like &lt;code&gt;/user/navendu&lt;/code&gt; will match this route. The example code below should give more clarity to these ideas.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;APISIX uses the &lt;a href="https://github.com/api7/lua-resty-radixtree"&gt;lua-resty-radixtree&lt;/a&gt; library, which wraps around &lt;a href="https://github.com/antirez/rax"&gt;rax&lt;/a&gt;, a radix tree implementation in C. This improves the performance compared to implementing the library in pure Lua.&lt;/p&gt;

&lt;p&gt;The example below shows how the library is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;radix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"resty.radixtree"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;rx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;radix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"/api/*action"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"metadata /api/action"&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="n"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"/user/:name"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"metadata /user/name"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"GET"&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="n"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"/admin/:name"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"metadata /admin/name"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"PUT"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;filter_fun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arg_access"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- matches the first route&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/api/create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;-- metadata /api/action&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"action: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;-- action: create&lt;/span&gt;

&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/api/destroy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;-- metadata /api/action&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"action: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;-- action: destroy&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- matches the second route&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/user/bobur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;-- metadata /user/name&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"name: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;-- name: bobur&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- matches the third route&lt;/span&gt;
&lt;span class="c1"&gt;-- the value for `arg_access` is obtained from `ngx.var`&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/admin/nicolas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;-- metadata /admin/name&lt;/span&gt;
&lt;span class="n"&gt;ngx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"admin name: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;-- admin name: nicolas&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ability to manage a large number of routes efficiently has made APISIX the API gateway of choice for &lt;a href="https://api7.ai/category/usercase"&gt;many large-scale projects&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look under the Hood
&lt;/h2&gt;

&lt;p&gt;There is only so much I can explain about the inner workings of APISIX in one article.&lt;/p&gt;

&lt;p&gt;But the best part is that the libraries mentioned here and Apache APISIX are &lt;a href="https://github.com/apache/apisix/"&gt;entirely open source&lt;/a&gt;, meaning you can look under the hood and modify things yourself.&lt;/p&gt;

&lt;p&gt;And if you can improve APISIX to get that final bit of performance, you can &lt;a href="https://apisix.apache.org/docs/general/contributor-guide/"&gt;contribute the changes&lt;/a&gt; back to the project and let everyone benefit from your work.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>performance</category>
      <category>api</category>
      <category>architecture</category>
    </item>
    <item>
      <title>A Comprehensive Guide to API Gateways, Kubernetes Gateways, and Service Meshes</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Fri, 09 Jun 2023 06:18:17 +0000</pubDate>
      <link>https://dev.to/apisix/a-comprehensive-guide-to-api-gateways-kubernetes-gateways-and-service-meshes-34d4</link>
      <guid>https://dev.to/apisix/a-comprehensive-guide-to-api-gateways-kubernetes-gateways-and-service-meshes-34d4</guid>
      <description>&lt;p&gt;&lt;strong&gt;Translations&lt;/strong&gt;: &lt;a href="https://lib.jimmysong.io/blog/gateway-and-mesh/" rel="noopener noreferrer"&gt;Simplified Chinese&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is still a lot of confusion about API gateways, Kubernetes gateways, and service meshes. A lot of this is because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;People often mention these technologies with the same keywords, like canary deployments, rate limiting, and service discovery.&lt;/li&gt;
&lt;li&gt;All these technologies use reverse proxies.&lt;/li&gt;
&lt;li&gt;Some API gateways have their own Kubernetes gateways and service meshes and vice versa.&lt;/li&gt;
&lt;li&gt;There are a lot of articles/videos that compare the three technologies and conclude why one is better than the other.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this article, I will try to explain these technologies and share how they fundamentally differ and cater to different use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Gateways
&lt;/h2&gt;

&lt;p&gt;An API gateway sits between your client applications and your APIs. It accepts all client requests, forwards them to the required APIs, and returns the response to clients in a combined package.&lt;/p&gt;

&lt;p&gt;It is basically a reverse proxy with a lot of capabilities.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F9FOZULBy_api-gateway.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F9FOZULBy_api-gateway.png" alt="API gateway"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On top of this, an API gateway can also have features like authentication, security, fine-grained traffic control, and monitoring, leaving the API developers to focus only on business needs.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://landscape.cncf.io/card-mode?category=api-gateway&amp;amp;grouping=category" rel="noopener noreferrer"&gt;many API gateway solutions available&lt;/a&gt;. Some of the popular free and open source solutions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/apache/apisix" rel="noopener noreferrer"&gt;Apache APISIX&lt;/a&gt;&lt;/strong&gt;: A high-performance, extensible, cloud native API gateway built on top of Nginx.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/solo-io/gloo" rel="noopener noreferrer"&gt;Gloo Edge&lt;/a&gt;&lt;/strong&gt;: An API gateway built on top of Envoy proxy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/kong/kong" rel="noopener noreferrer"&gt;Kong&lt;/a&gt;&lt;/strong&gt;: A pluggable API gateway also built on Nginx.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/TykTechnologies/tyk" rel="noopener noreferrer"&gt;Tyk&lt;/a&gt;&lt;/strong&gt;: An API gateway written in Go supporting REST, GraphQL, TCP, and gRPC protocols.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloud platforms like &lt;a href="https://cloud.google.com/api-gateway" rel="noopener noreferrer"&gt;GCP&lt;/a&gt;, &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, and &lt;a href="https://learn.microsoft.com/en-us/azure/api-management/" rel="noopener noreferrer"&gt;Azure&lt;/a&gt; also have their own proprietary API gateways.&lt;/p&gt;

&lt;p&gt;API gateways, Kubernetes gateways, and service meshes support canary deployments—gradually rolling out a new software version to a small subset of users before making it generally available.&lt;/p&gt;

&lt;p&gt;The example below shows how to configure a canary deployment in Apache APISIX.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FVGolFz8X_canary-api-gateway.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FVGolFz8X_canary-api-gateway.png" alt="Canary deployments with an API gateway"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can send a request to the &lt;a href="https://apisix.apache.org/docs/apisix/admin-api/" rel="noopener noreferrer"&gt;APISIX Admin API&lt;/a&gt; with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:9180/apisix/admin/routes/1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'
{
  "uri":"/*",
  "plugins":{
    "traffic-split":{
      "rules":[
        {
          "weighted_upstreams":[
            {
              "upstream":{
                "name":"api-v1",
                "type":"roundrobin",
                "nodes":{
                  "api-v1:8080":1
                }
              },
              "weight":95
            },
            {
              "weight":5
            }
          ]
        }
      ]
    }
  },
  "upstream":{
    "type":"roundrobin",
    "nodes":{
      "api-v2:8080":1
    }
  }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;APISIX will now route 95% of the traffic to the api-v1 service and 5% to the api-v2 service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes Gateways
&lt;/h2&gt;

&lt;p&gt;Kubernetes gateways are just Kubernetes-native API gateways. i.e., you can manage these API gateways with the Kubernetes API, similar to a Kubernetes pod, service, or deployment.&lt;/p&gt;

&lt;p&gt;In Kubernetes, your APIs are pods and services deployed in a cluster. You then use a Kubernetes gateway to direct external traffic to your cluster.&lt;/p&gt;

&lt;p&gt;Kubernetes provides two APIs to achieve this, the &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" rel="noopener noreferrer"&gt;Ingress API&lt;/a&gt; and the &lt;a href="https://gateway-api.sigs.k8s.io/" rel="noopener noreferrer"&gt;Gateway API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FLCsVmIcW_kubernetes-gateway.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FLCsVmIcW_kubernetes-gateway.png" alt="Kubernetes gateway"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubernetes Ingress API
&lt;/h3&gt;

&lt;p&gt;The Ingress API was created to overcome the limitations of the default service types, &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport" rel="noopener noreferrer"&gt;NodePort&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer" rel="noopener noreferrer"&gt;LoadBalancer&lt;/a&gt;, by introducing features like routing and SSL termination. It also standardized how you expose Kubernetes services to external traffic.&lt;/p&gt;

&lt;p&gt;It has two components, the &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource" rel="noopener noreferrer"&gt;Ingress&lt;/a&gt; and the &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/" rel="noopener noreferrer"&gt;Ingress controller&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Ingress Kubernetes native object defines a set of rules on how external traffic can access your services.&lt;/p&gt;

&lt;p&gt;This example configuration shows routing traffic based on URI path with the Kubernetes Ingress object:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-routes&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;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apisix&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&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="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&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;api-v1&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Exact&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&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;api-v2&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v2&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Exact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An Ingress controller implements these rules and routes traffic to your cluster using a reverse proxy.&lt;/p&gt;

&lt;p&gt;There are over &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/#additional-controllers" rel="noopener noreferrer"&gt;20 Ingress controller implementations&lt;/a&gt;. APISIX has an &lt;a href="https://apisix.apache.org/docs/ingress-controller/next/getting-started/" rel="noopener noreferrer"&gt;Ingress controller&lt;/a&gt; that wraps around APISIX API gateway to work as Kubernetes Ingress.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FdyKXKxD8_apisix-ingress.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FdyKXKxD8_apisix-ingress.png" alt="APISIX Ingress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The APISIX Ingress controller converts the Kubernetes Ingress object to APISIX configuration.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F3OrSWecX_translate.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F3OrSWecX_translate.png" alt="APISIX Ingress controller translates configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;APISIX then implements this configuration.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2Fjqnm8CEi_route-ingress.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2Fjqnm8CEi_route-ingress.png" alt="Canary deployments with Kubernetes Ingress API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can swap APISIX with any other Ingress controller, as the Ingress API is not tied to any specific implementation.&lt;/p&gt;

&lt;p&gt;This vendor neutrality works well for simple configurations. But if you want to do complex routing like a canary deployment, you must rely on vendor-specific annotations.&lt;/p&gt;

&lt;p&gt;The example below shows how to configure a canary deployment using &lt;a href="https://docs.nginx.com/nginx-ingress-controller/" rel="noopener noreferrer"&gt;Nginx Ingress&lt;/a&gt;. The &lt;a href="https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#canary" rel="noopener noreferrer"&gt;custom annotations&lt;/a&gt; used here are specific to Nginx:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/canary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/canary-weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5"&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;api-canary&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;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&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="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-v2&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;path&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;The above configuration will route 5% of the traffic to the api-v2 service.&lt;/p&gt;

&lt;p&gt;In addition to annotations, Ingress controllers like APISIX have custom Kubernetes CRDs to overcome the limitations of the Ingress API.&lt;/p&gt;

&lt;p&gt;The example below uses the APISIX CRD, &lt;a href="https://apisix.apache.org/docs/ingress-controller/concepts/apisix_route/" rel="noopener noreferrer"&gt;ApisixRoute&lt;/a&gt; to configure a canary deployment:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apisix.apache.org/v2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApisixRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-canary&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;http&lt;/span&gt;&lt;span class="pi"&gt;:&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;route&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&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;/*&lt;/span&gt;
      &lt;span class="na"&gt;backends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-v1&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;95&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-v2&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These custom CRDs made it much easier to configure Ingress and leverage the full capabilities of the API gateway underneath but at the expense of portability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubernetes Gateway API
&lt;/h3&gt;

&lt;p&gt;The Gateway API is a new Kubernetes object that aims to "fix" the Ingress API.&lt;/p&gt;

&lt;p&gt;It takes inspiration from the custom CRDs developed by Ingress controllers to add HTTP header-based matching, weighted traffic splitting, and &lt;a href="https://gateway-api.sigs.k8s.io/#gateway-api-concepts" rel="noopener noreferrer"&gt;other features&lt;/a&gt; that require custom proprietary annotations with the Ingress API.&lt;/p&gt;

&lt;p&gt;The example below shows configuring a canary deployment with the Kubernetes Gateway API:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway.networking.k8s.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-canary&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;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;backendRefs&lt;/span&gt;&lt;span class="pi"&gt;:&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;api-v1&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;95&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;api-v2&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any Ingress controller (that implements the Gateway API) can now implement this configuration.&lt;/p&gt;

&lt;p&gt;The Gateway API also makes &lt;a href="https://dev.to/posts/gateway-vs-ingress-api/"&gt;many improvements&lt;/a&gt; over the Ingress API, but it is still in alpha, and the Gateway API implementations are constantly breaking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Meshes
&lt;/h2&gt;

&lt;p&gt;API gateways and Kubernetes gateways work across application boundaries solving edge problems while abstracting your APIs.&lt;/p&gt;

&lt;p&gt;Service Meshes solve a different challenge.&lt;/p&gt;

&lt;p&gt;A service mesh is more concerned about inter-service communication (east-west traffic) than service-client communication (north-south traffic).&lt;/p&gt;

&lt;p&gt;Typically, this is achieved by deploying sidecar proxies with APIs/services.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F2tyGtXUE_service-mesh.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F2tyGtXUE_service-mesh.png" alt="Service mesh"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the sidecar proxies handle the service-to-service communication instead of the developer having to code the networking logic to the services.&lt;/p&gt;

&lt;p&gt;There are a lot of service meshes available. Some of the popular ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt;&lt;/strong&gt;: By far the most popular service mesh. It is built on top of &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy proxy&lt;/a&gt;, which many service meshes use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://linkerd.io/" rel="noopener noreferrer"&gt;Linkerd&lt;/a&gt;&lt;/strong&gt;: A lightweight service mesh that uses linkerd2-proxy, written in Rust specifically for Linkerd.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt;&lt;/strong&gt;: A service mesh emphasizing security and observability. It can work with either a built-in proxy or Envoy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New service mesh offerings like &lt;a href="https://isovalent.com/blog/post/introducing-cilium-mesh/" rel="noopener noreferrer"&gt;Cilium&lt;/a&gt; offer alternatives to sidecar-based service meshes by using networking capabilities directly from the kernel through &lt;a href="https://ebpf.io/" rel="noopener noreferrer"&gt;eBPF&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FDxQSIjRv_cilium-mesh.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FDxQSIjRv_cilium-mesh.png" alt="Sidecar-less service mesh"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A typical service mesh requires 8 proxies for 8 services whereas eBPF-based service meshes like Cilium don't. Adapted from &lt;a href="https://isovalent.com/blog/post/cilium-service-" rel="noopener noreferrer"&gt;Cilium Service Mesh – Everything You Need to Know&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Service meshes also have basic ingress/egress gateways to handle north-south traffic to and from the services. Ingress gateways are the entry points of external traffic to a service mesh, and egress gateways allow services inside a mesh to access external services.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FHVIIszQZ_ingress-egress-mesh.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2FHVIIszQZ_ingress-egress-mesh.png" alt="Ingress and egress gateways with a service mesh"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apache APISIX also has a service mesh implementation called &lt;a href="https://github.com/api7/Amesh" rel="noopener noreferrer"&gt;Amesh&lt;/a&gt;. It works with Istio's control plane using the xDS protocol replacing the default Envoy proxy in the sidecar.&lt;/p&gt;

&lt;p&gt;A service mesh lets you configure canary deployments. For example, you can split the requests from one service between two versions of another service.&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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F0yhZX5Px_canary-mesh.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%2Fstatic.apiseven.com%2Fuploads%2F2023%2F06%2F07%2F0yhZX5Px_canary-mesh.png" alt="Canary deployments with a service mesh"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example below shows configuring a &lt;a href="https://istio.io/latest/docs/concepts/traffic-management/" rel="noopener noreferrer"&gt;canary deployment with Istio service mesh&lt;/a&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1alpha3&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VirtualService&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-virtual-service&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;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
  &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
            &lt;span class="na"&gt;subset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
            &lt;span class="na"&gt;subset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v2&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1alpha3&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DestinationRule&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-destination-rule&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;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
  &lt;span class="na"&gt;subsets&lt;/span&gt;&lt;span class="pi"&gt;:&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;v1&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&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;v2&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These configurations are specific to Istio. To switch to a different service mesh, you must create a different but similarly vendor-dependant configuration.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://smi-spec.io/" rel="noopener noreferrer"&gt;Service Mesh Interface&lt;/a&gt; (SMI) specification was created to solve this portability issue.&lt;/p&gt;

&lt;p&gt;The SMI &lt;a href="https://github.com/servicemeshinterface/smi-spec" rel="noopener noreferrer"&gt;spec&lt;/a&gt; is a set of Kubernetes CRDs that a service mesh user can use to define applications without binding to service mesh implementations.&lt;/p&gt;

&lt;p&gt;A standardization attempt will only work if all the projects are on board. But this did not happen with the SMI spec, and &lt;a href="https://layer5.io/service-mesh-landscape#smi" rel="noopener noreferrer"&gt;only a few projects participated actively&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;More recently, the &lt;a href="https://github.com/kubernetes/community/tree/master/sig-network" rel="noopener noreferrer"&gt;Kubernetes SIG Network&lt;/a&gt; has been evolving the Gateway API to support service meshes.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://gateway-api.sigs.k8s.io/contributing/gamma/" rel="noopener noreferrer"&gt;GAMMA (Gateway API for Mesh Management and Administration) initiative&lt;/a&gt; is a dedicated group with the Gateway API project with goals to "investigate, design, and track Gateway API resources, semantics, and other artifacts related to service mesh technology and use-cases."&lt;/p&gt;

&lt;p&gt;Gateway API is a natural next step to the Ingress API, but we must wait to see how it will work for service meshes. Istio &lt;a href="https://istio.io/latest/blog/2022/gateway-api-beta/" rel="noopener noreferrer"&gt;has announced&lt;/a&gt; its intention to use the Gateway API as its default API for all traffic management and continues to drive the project forward.&lt;/p&gt;

&lt;p&gt;The example below shows &lt;a href="https://istio.io/latest/docs/tasks/traffic-management/traffic-shifting/" rel="noopener noreferrer"&gt;configuring a canary deployment in Istio with the Gateway API&lt;/a&gt;. The underlying idea is using &lt;a href="https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ParentReference" rel="noopener noreferrer"&gt;parentRefs&lt;/a&gt; to attach to other services instead of the gateway:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway.networking.k8s.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-canary&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;parentRefs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&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;api-a&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;backendRefs&lt;/span&gt;&lt;span class="pi"&gt;:&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;api-b-v1&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;95&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;api-b-v2&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are &lt;a href="https://thenewstack.io/the-gateway-api-is-in-the-firing-line-of-the-service-mesh-wars/" rel="noopener noreferrer"&gt;some concerns&lt;/a&gt; that the GAMMA project might become skewed to serve the needs of one particular project than the larger community, which will eventually lead to other projects using their own APIs, similar to the &lt;a href="https://dev.to/posts/gateway-vs-ingress-api/#custom-crds--ingress-api"&gt;custom CRD scenario&lt;/a&gt; after the Kubernetes Ingress API.&lt;/p&gt;

&lt;p&gt;But the Gateway API project has been the best attempt at standardizing traffic management in service meshes. The &lt;a href="https://smi-spec.io/blog/announcing-smi-gateway-api-gamma/" rel="noopener noreferrer"&gt;SMI project also joined the GAMMA initiative&lt;/a&gt; with a shared vision and will help advocate for consistent implementations of the Gateway API by service mesh projects.&lt;/p&gt;

&lt;p&gt;Other projects like &lt;a href="https://docs.flagger.app/tutorials/gatewayapi-progressive-delivery" rel="noopener noreferrer"&gt;Flagger&lt;/a&gt; and &lt;a href="https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi" rel="noopener noreferrer"&gt;Argo Rollouts&lt;/a&gt; have also integrated with the Gateway API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should You Use?
&lt;/h2&gt;

&lt;p&gt;There is only one correct answer to this question; "it depends."&lt;/p&gt;

&lt;p&gt;If you are developing APIs and need authentication, security, routing, or metrics, you are better off using an API gateway than building this on your own in your APIs.&lt;/p&gt;

&lt;p&gt;If you want to do something similar in a Kubernetes environment, you should use a Kubernetes gateway instead of trying to wrangle your API gateway to work on Kubernetes. Thankfully, a lot of API gateways also work with Kubernetes-native configurations.&lt;/p&gt;

&lt;p&gt;But sometimes, the features offered by an API gateway + Ingress controller might be an overkill for a Kubernetes environment, and you might want to switch back to simple traffic management.&lt;/p&gt;

&lt;p&gt;Service meshes, on the other hand, solve an entirely different set of problems. They also bring their own gateways to handle north-south traffic (usually enough) but also let you use your own gateways with more features.&lt;/p&gt;

&lt;p&gt;The convergence of the API gateway and the service mesh through the Kubernetes Gateway API should make it easier for the application developer to focus on solving problems than worry about the underlying implementation.&lt;/p&gt;

&lt;p&gt;Projects like Apache APISIX use the same technology to build the API gateway and service mesh offerings and integrate well with these specifications, incentivizing vendor-neutral choices.&lt;/p&gt;

&lt;p&gt;It is also likely that you will not need any of these. You may &lt;a href="https://blog.frankel.ch/chopping-monolith/" rel="noopener noreferrer"&gt;not even need microservices&lt;/a&gt; or a distributed architecture, but when you need them, gateways and meshes can make your lives a lot easier.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>kubernetes</category>
      <category>apigateway</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Comparing Kubernetes Gateway and Ingress APIs</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Wed, 01 Feb 2023 04:44:24 +0000</pubDate>
      <link>https://dev.to/apisix/comparing-kubernetes-gateway-and-ingress-apis-22oj</link>
      <guid>https://dev.to/apisix/comparing-kubernetes-gateway-and-ingress-apis-22oj</guid>
      <description>&lt;p&gt;A couple of months ago, the new &lt;a href="https://kubernetes.io/blog/2022/07/13/gateway-api-graduates-to-beta/" rel="noopener noreferrer"&gt;Kubernetes Gateway API graduated to beta&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why do you need another API to handle external traffic when you have the stable &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/#alternatives" rel="noopener noreferrer"&gt;Kubernetes Ingress API&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/#additional-controllers" rel="noopener noreferrer"&gt;dozens of implementations&lt;/a&gt;? What problems of the Ingress API does the new Gateway API solve? Does this mean the end of the Ingress API?&lt;/p&gt;

&lt;p&gt;I will try to answer these questions in this article by getting hands-on with these APIs and looking at how they evolved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standardizing External Access to Services: The Ingress API
&lt;/h2&gt;

&lt;p&gt;The Kubernetes Ingress API was created to standardize exposing services in Kubernetes to external traffic. The Ingress API overcame the limitations of the default service types, &lt;code&gt;NodePort&lt;/code&gt; and &lt;code&gt;LoadBalancer&lt;/code&gt;, by introducing features like routing and SSL termination.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fs4q64tux04u2nk9clrhe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fs4q64tux04u2nk9clrhe.png" alt="Kubernetes Ingress" width="412" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are over &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/#additional-controllers" rel="noopener noreferrer"&gt;20 implementations of Ingress controllers&lt;/a&gt; available. In this article, I will use &lt;a href="https://apisix.apache.org/" rel="noopener noreferrer"&gt;Apache APISIX&lt;/a&gt; and its &lt;a href="https://apisix.apache.org/docs/ingress-controller/next/getting-started/" rel="noopener noreferrer"&gt;Ingress controller&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F52ho5rqhxzq87axnrrkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F52ho5rqhxzq87axnrrkt.png" alt="APISIX Ingress controller" width="605" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create an &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource" rel="noopener noreferrer"&gt;Ingress resource&lt;/a&gt; to configure APISIX or any other Ingress implementations.&lt;/p&gt;

&lt;p&gt;The example below shows how you can route traffic between two versions of an application with APISIX Ingress:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-routes&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;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apisix&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local.navendu.me&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&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="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&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;bare-minimum-api-v1&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&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;bare-minimum-api-v2&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v2&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: You can check out this &lt;a href="https://navendu.me/posts/hands-on-set-up-ingress-on-kubernetes-with-apache-apisix-ingress-controller/" rel="noopener noreferrer"&gt;hands-on tutorial&lt;/a&gt; to learn more about setting up Ingress on Kubernetes with Apache APISIX Ingress controller.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since the Ingress API is not tied to any particular controller implementation, you can swap APISIX with any other Ingress controller, and it will work similarly.&lt;/p&gt;

&lt;p&gt;This is okay for simple routing. But the API is limited, and if you want to use the full features provided by your Ingress controller, you are stuck with &lt;a href="https://apisix.apache.org/docs/ingress-controller/concepts/annotations/" rel="noopener noreferrer"&gt;annotations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, the Kubernetes Ingress API does not provide a schema to configure rewrites. Rewrites are useful when your upstream/backend URL differs from the path configured in your Ingress rule.&lt;/p&gt;

&lt;p&gt;APISIX supports this feature, and you have to use custom annotations to leverage it:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-routes&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;k8s.apisix.apache.org/rewrite-target-regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/app/(.*)"&lt;/span&gt;
    &lt;span class="na"&gt;k8s.apisix.apache.org/rewrite-target-regex-template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/$1"&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;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apisix&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local.navendu.me&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&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="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&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;bare-minimum-api&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates an Ingress resource that configures APISIX to route any requests with the &lt;code&gt;/app&lt;/code&gt; prefix to the backend with the prefix removed. For example, a request to &lt;code&gt;/app/version&lt;/code&gt; will be forwarded to &lt;code&gt;/version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Annotations are specific to your choice of an Ingress controller. These "proprietary" extensions limited the scope of portability intended initially with the Ingress API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom CRDs &amp;gt; Ingress API
&lt;/h2&gt;

&lt;p&gt;Being stuck with annotations also sacrifice the usability of the Ingress controllers.&lt;/p&gt;

&lt;p&gt;Controllers therefore solved the limitations of the Ingress API by creating their &lt;a href="https://apisix.apache.org/docs/ingress-controller/references/apisix_route_v2/" rel="noopener noreferrer"&gt;own custom resources&lt;/a&gt;. The example below shows configuring Ingress to route traffic between two versions of an application using APISIX's custom resource:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apisix.apache.org/v2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApisixRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;api-routes&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;http&lt;/span&gt;&lt;span class="pi"&gt;:&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;route-1&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;local.navendu.me&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;/v1&lt;/span&gt;
      &lt;span class="na"&gt;backends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bare-minimum-api-v1&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&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;route-2&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;local.navendu.me&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;/v2&lt;/span&gt;
      &lt;span class="na"&gt;backends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bare-minimum-api-v2&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These CRDs made it much easier to configure Ingress, but you are tied to the specific Ingress control implementation. Without the Ingress API evolving, you had to choose between usability or portability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending Ingress and Evolution to Gateway API
&lt;/h2&gt;

&lt;p&gt;Ingress API was not broken; it was limited. The Gateway API was designed to overcome these limitations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(Gateway API) aim to evolve Kubernetes service networking through expressive, extensible, and role-oriented interfaces ...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It takes inspiration from the custom CRDs of different Ingress controllers mentioned earlier.&lt;/p&gt;

&lt;p&gt;The Gateway API adds &lt;a href="https://gateway-api.sigs.k8s.io/#gateway-api-concepts" rel="noopener noreferrer"&gt;many features&lt;/a&gt; "on top" of the Ingress API's capabilities. This includes HTTP header-based matching, weighted traffic splitting, and other features that require custom proprietary annotations with the Ingress API.&lt;/p&gt;

&lt;p&gt;Traffic split with APISIX Ingress resource (see &lt;a href="https://apisix.apache.org/docs/ingress-controller/references/apisix_route_v2/" rel="noopener noreferrer"&gt;ApisixRoute/v2 reference&lt;/a&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apisix.apache.org/v2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApisixRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;traffic-split&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;http&lt;/span&gt;&lt;span class="pi"&gt;:&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;rule-1&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;local.navendu.me&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;/get*&lt;/span&gt;
      &lt;span class="na"&gt;backends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bare-minimum-api-v1&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bare-minimum-api-v2&lt;/span&gt;
          &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Traffic split with Gateway API (see &lt;a href="https://gateway-api.sigs.k8s.io/guides/traffic-splitting/#canary-traffic-rollout" rel="noopener noreferrer"&gt;Canary traffic rollout&lt;/a&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway.networking.k8s.io/v1alpha2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;traffic-split&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;hostnames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;local.navendu.me&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;backendRefs&lt;/span&gt;&lt;span class="pi"&gt;:&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;bare-minimum-api-v1&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&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;bare-minimum-api-v2&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another improvement from the Ingress API is how the Gateway API &lt;a href="https://gateway-api.sigs.k8s.io/concepts/security-model/#roles-and-personas" rel="noopener noreferrer"&gt;separates concerns&lt;/a&gt;. With Ingress, the application developer and the cluster operator work on the same Ingress object, unaware of the other's responsibilities and opening the door for misconfigurations.&lt;/p&gt;

&lt;p&gt;The Gateway API separates the configurations into Route and Gateway objects providing autonomy for the application developer and the cluster operator. The diagram below explains this clearly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzbm14n33w7j7zyin3grx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzbm14n33w7j7zyin3grx.png" alt="Separation of concerns in Gateway API" width="568" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is This the End of Ingress API?
&lt;/h2&gt;

&lt;p&gt;The Gateway API is relatively new, and its implementations are constantly breaking. On the contrary, the Ingress API is in stable release and has stood the test of time.&lt;/p&gt;

&lt;p&gt;If your use case only involves simple routing and if you are okay with using custom annotations to get extra features, the Ingress API is still a solid choice.&lt;/p&gt;

&lt;p&gt;With the Gateway API being a superset of the Ingress API, it might make sense to consolidate both. Thanks to the &lt;a href="https://github.com/kubernetes/community/tree/master/sig-network" rel="noopener noreferrer"&gt;SIG Network&lt;/a&gt; community, Gateway API is still growing and will soon be production ready.&lt;/p&gt;

&lt;p&gt;Most Ingress controllers and &lt;a href="https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/" rel="noopener noreferrer"&gt;service meshes&lt;/a&gt; have already implemented the Gateway API along with the Ingress API, and as the project evolves, more implementations will surface.&lt;/p&gt;

&lt;p&gt;Personally, at least for now, I would stick with &lt;a href="https://apisix.apache.org/docs/ingress-controller/next/getting-started/" rel="noopener noreferrer"&gt;custom CRDs&lt;/a&gt; provided by the Ingress controllers like Apache APISIX instead of the Ingress or Gateway API.&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>blockchain</category>
      <category>offers</category>
      <category>web3</category>
    </item>
    <item>
      <title>Why Should You Contribute to Open Source? - Contributing to Open Source #1</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Wed, 31 Aug 2022 12:24:41 +0000</pubDate>
      <link>https://dev.to/pottekkat/why-should-you-contribute-to-open-source-contributing-to-open-source-1-1de0</link>
      <guid>https://dev.to/pottekkat/why-should-you-contribute-to-open-source-contributing-to-open-source-1-1de0</guid>
      <description>&lt;p&gt;In this series of articles, I will share how you can be an open source contributor and make a career out of it.&lt;/p&gt;

&lt;p&gt;I have been contributing to, building, and maintaining open source projects for the past three years. Everything I mention in this series is from my personal experience.&lt;/p&gt;

&lt;p&gt;We will start the series by talking about &lt;em&gt;why&lt;/em&gt; you should contribute to open source.&lt;/p&gt;

&lt;p&gt;Understanding the "why" is essential as you will spend your free time and a lot effort to contribute to open source projects. Without clear goals, you are likely to end up wasting your time.&lt;/p&gt;

&lt;p&gt;So, here are some reasons why you should consider contributing to open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improve Your Skills
&lt;/h2&gt;

&lt;p&gt;Learning new skills and improving existing ones is important if you are a student or a new developer.&lt;/p&gt;

&lt;p&gt;While contributing to an open source project, you can implement your skills and improve them. You can learn from seasoned contributors in the community and have them review your contributions.&lt;/p&gt;

&lt;p&gt;Even if you are not contributing code, you can improve other skills like writing, designing, managing, and more. There will always be a place for your talent in an open source project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gain Real World Experience
&lt;/h2&gt;

&lt;p&gt;You do not have to go through a whiteboard interview to contribute to an open source project.&lt;/p&gt;

&lt;p&gt;If you are starting your career, you can contribute to an open source project and gain actual work experience. Instead of internships and boring jobs, work on top open source projects used by millions of people.&lt;/p&gt;

&lt;p&gt;Also, you don't have to limit yourself to your city or country. You can choose to work on any open source project around the world, right from your home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gain Recognition
&lt;/h2&gt;

&lt;p&gt;Your contributions to open source is public. This means more people can see your work and recognize you for it.&lt;/p&gt;

&lt;p&gt;Even if you don't become an open source superstar, building in public will do you good. What's better than hearing, "Oh, you are a contributor to ABC? I love ABC!".&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Your Network
&lt;/h2&gt;

&lt;p&gt;Open source projects revolve around open source communities.&lt;/p&gt;

&lt;p&gt;Being part of a community and contributing to a project is the best way to meet like-minded people and grow your network.&lt;/p&gt;

&lt;p&gt;For your career, these networks can be game changers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find New Opportunities
&lt;/h2&gt;

&lt;p&gt;With improved skills, real-world experience, braggable contributions, and a solid network, more opportunities will find you.&lt;/p&gt;

&lt;p&gt;You can continue to work in open source or showcase your open source contributions to recruiters. You can use your network to find jobs. And when they ask if you have experience, you can send them a link to your GitHub profile.&lt;/p&gt;




&lt;p&gt;When working in open source, you are working with some of the smartest people, learning by looking through vetted lines of code, and contributing to something that a lot of people could use.&lt;/p&gt;

&lt;p&gt;As open source projects become sustainable, working on one is turning out to be a stable career option. In the following article, you will learn how you can find a project to contribute to.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>codenewbie</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Best Practices for Building Reliable APIs</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Fri, 26 Aug 2022 04:59:00 +0000</pubDate>
      <link>https://dev.to/apisix/best-practices-for-building-reliable-apis-1194</link>
      <guid>https://dev.to/apisix/best-practices-for-building-reliable-apis-1194</guid>
      <description>&lt;p&gt;As your APIs scale, the need for making them reliable and robust increases.&lt;/p&gt;

&lt;p&gt;This article discusses the best practices for building reliable APIs by introducing a special kind of reverse proxies called API gateways.&lt;/p&gt;

&lt;p&gt;We will look into:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Problems with traditional API designs&lt;/li&gt;
&lt;li&gt; What API gateways are&lt;/li&gt;
&lt;li&gt; How API gateways improve APIs and&lt;/li&gt;
&lt;li&gt; Patterns and examples using API gateways&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But first, what are "reliable" APIs?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes an API Reliable?
&lt;/h2&gt;

&lt;p&gt;As a service provider, you might have service-level agreements (SLAs) with your customers, usually quoted in uptime---the amount of time the service is guaranteed to be online and operational.&lt;/p&gt;

&lt;p&gt;Uptime is a myopic view of reliability. To understand what it means to be reliable, you have to look at the factors that affect uptime. Once you understand these factors, you will be in a better position to build reliable services.&lt;/p&gt;

&lt;p&gt;Let's look at these factors and the questions they pose:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Latency&lt;/strong&gt;: How fast does your API respond to requests?&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Security&lt;/strong&gt;: Who can access your API? Is it secure?&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Downtime&lt;/strong&gt; Frequency: How frequently is your API down?&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Consistency&lt;/strong&gt;: Are your API endpoints constant? Do consumers need to change their code often?&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Monitoring and Reporting&lt;/strong&gt;: Can you observe issues and failures in your API? Are you reporting them to your consumers?&lt;/li&gt;
&lt;/ol&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-084123.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-084123.png" title="api" alt="network error/api.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As organizations move to cloud native architectures, it becomes difficult for the development teams to account for these factors on each of their services. And as these systems scale, it would be much easier to delegate these responsibilities to a single, separate system. &lt;em&gt;Say hello to API gateways!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  API Gateway, the Unified Entrypoint
&lt;/h2&gt;

&lt;p&gt;An API gateway acts as a middleman between your clients and your APIs. It will accept all traffic (API calls) like reverse proxies, forwards the request to the required services in your backend, and returns the needed results.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-101223.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-101223.png" title="api gateway" alt="network error/api gateway.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An API gateway can be the central point that handles all the authentication, security, traffic control, and monitoring concerns, leaving the API developers to focus on business needs and making it easier to improve reliability.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093336.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093336.png" title="api gateway reliability" alt="network error/api gateway reliability.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of &lt;a href="https://geekflare.com/api-gateway/" rel="noopener noreferrer"&gt;open source and managed API gateway offerings&lt;/a&gt; available. In this article, I will be using &lt;a href="https://apisix.apache.org/" rel="noopener noreferrer"&gt;Apache APISIX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following section will describe some of the best practices to make your APIs reliable using API gateways.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliability Best Practices with API Gateways
&lt;/h2&gt;

&lt;p&gt;We will focus more on the pattern underneath than the actual implementation, as it can vary based on your API gateway choice.&lt;/p&gt;

&lt;p&gt;I will divide these patterns into three categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Authentication and security&lt;/li&gt;
&lt;li&gt; Monitoring and observability&lt;/li&gt;
&lt;li&gt; Version control and zero downtime&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will look into each category in detail below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication and Security
&lt;/h3&gt;

&lt;h4&gt;
  
  
  User Authentication
&lt;/h4&gt;

&lt;p&gt;Authenticated requests with API gateways secure client-API interactions. After a client authenticates, your API gateway can use the obtained client details for fine-grained control.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093420.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093420.png" title="user authentication" alt="network error/user authentication.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;APISIX handles authentication directly through plugins like &lt;a href="https://apisix.apache.org/docs/apisix/plugins/key-auth/" rel="noopener noreferrer"&gt;key-auth&lt;/a&gt; and &lt;a href="https://apisix.apache.org/docs/apisix/plugins/jwt-auth/" rel="noopener noreferrer"&gt;jwt-auth&lt;/a&gt;. APISIX also supports OAuth authentication and role-based access control systems like wolf through plugins like &lt;a href="https://apisix.apache.org/docs/apisix/plugins/openid-connect/" rel="noopener noreferrer"&gt;openid-connect&lt;/a&gt; and &lt;a href="https://apisix.apache.org/docs/apisix/plugins/wolf-rbac/" rel="noopener noreferrer"&gt;wolf-rbac&lt;/a&gt;, respectively.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rate Limiting
&lt;/h4&gt;

&lt;p&gt;Intentional (DoS attacks) and unintentional (clients making too many requests) traffic spikes to your APIs can bring them down like a house of cards. Setting up rate limiting will improve the reliability of your systems in handling such scenarios.&lt;/p&gt;

&lt;p&gt;You can set up rate limiting on your API gateway, and if the number of requests increases above a threshold, the API gateway could either delay or reject the exceeding requests.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093445.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093445.png" title="rate limiting" alt="network error/rate limiting.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With APISIX, you can use any of the three plugins to configure rate limits based on number of requests, number of concurrent requests per client, and count (&lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/limit-req/" rel="noopener noreferrer"&gt;limit-req&lt;/a&gt;, &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/limit-conn/" rel="noopener noreferrer"&gt;limit-conn&lt;/a&gt;, &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/limit-count/" rel="noopener noreferrer"&gt;limit-count&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and Observability
&lt;/h3&gt;

&lt;p&gt;Your API's reliability and your monitoring setup go hand in hand. And you can monitor your reliability metrics by setting up monitoring on your API gateway.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093513.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093513.png" title="monitoring and observability" alt="network error/monitoring and observability.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;API logs and traces provide detailed information about an API call. This information will help you know when your API has failed or has an error as soon as possible. Silent fails lead to unfixed errors which can cause problems in the future.&lt;/p&gt;

&lt;p&gt;With some configuration, you will also be able to predict and anticipate traffic for the future, helping you scale reliably.&lt;/p&gt;

&lt;p&gt;APISIX has plugins that integrate with logging (&lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/skywalking-logger/" rel="noopener noreferrer"&gt;Apache SkyWalking&lt;/a&gt;, &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/rocketmq-logger/" rel="noopener noreferrer"&gt;RocketMQ&lt;/a&gt;), metrics (&lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/prometheus/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;, &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/datadog/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt;), and tracing (&lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/opentelemetry/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt;, &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/zipkin/" rel="noopener noreferrer"&gt;Zipkin&lt;/a&gt;) platforms/specifications. You can read more on &lt;a href="https://apisix.apache.org/blog/2022/04/17/api-observability/" rel="noopener noreferrer"&gt;API Observability with APISIX Plugins&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version Control and Zero Downtime
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Canary Release
&lt;/h4&gt;

&lt;p&gt;When switching to new versions of your APIs, you must ensure that you don't drop your traffic. Clients should still be able to make requests to your API and get back the correct response.&lt;/p&gt;

&lt;p&gt;With an API gateway, you can setup canary releases. This will ensure that your API remains functional during the transition, and you can also roll back to the older version if there are any issues.&lt;/p&gt;

&lt;p&gt;Initially, the API gateway will route all traffic to your API's old version.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093542.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093542.png" title="old version" alt="network error/old version.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you have a new version, you can configure the API gateway to route some of your traffic to this new version. You can keep increasing the percentage of traffic to your new service and check if everything is working as expected.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093559.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093559.png" title="canary release" alt="network error/canary release.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, you can route all traffic to your new API.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-100732.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-100732.png" title="new api" alt="network error/new API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;APISIX uses the &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/traffic-split/" rel="noopener noreferrer"&gt;traffic-split&lt;/a&gt; plugin that lets you control the traffic to your services. You can use it to set up canary releases or your custom release configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  Circuit Breaking
&lt;/h4&gt;

&lt;p&gt;When one of your upstream services is unavailable or is experiencing high latency, it needs to be cut off from your system. Otherwise, the client will keep retrying the request, leading to resource exhaustion. This failure can creep into other services in your system and bring them down.&lt;/p&gt;

&lt;p&gt;Like how electrical circuit breakers isolate faulty components from a circuit, API gateways have a circuit breaker feature that disconnects faulty services, keeping the system healthy. Traffic to these services are rerouted or delayed until the service becomes healthy.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093714.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093714.png" title="circuit breaking" alt="network error/circuit breaking.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;APISIX comes with an &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/api-breaker/" rel="noopener noreferrer"&gt;api-breaker&lt;/a&gt; plugin that implements this pattern.&lt;/p&gt;

&lt;h4&gt;
  
  
  Redirects
&lt;/h4&gt;

&lt;p&gt;As you update your APIs, their endpoints might undergo some change. Traditionally, this would mean that the client application should send requests to the &lt;code&gt;/new-api-endpoint&lt;/code&gt; instead of the &lt;code&gt;/old-api-endpoint&lt;/code&gt;, meaning your consumers must manually change each call to this API endpoint.&lt;/p&gt;

&lt;p&gt;If unanticipated, this can break client applications.&lt;/p&gt;

&lt;p&gt;With an API gateway, you can provide an abstraction layer and redirect requests to the &lt;code&gt;/new-api-endpoint&lt;/code&gt; without having the clients change their requests. With proper redirect status codes and messages, you can gradually depreciate the &lt;code&gt;/old-api-endpoint&lt;/code&gt; without your consumers experiencing any downtime.&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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093729.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%2Fstatic.apiseven.com%2F2022%2Fblog%2F0819%2F2022-08-19-093729.png" title="redirect" alt="network error/redirect.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With APISIX, you can use the &lt;a href="https://apisix.apache.org/docs/apisix/next/plugins/redirect/" rel="noopener noreferrer"&gt;redirect&lt;/a&gt; plugin to configure redirects.&lt;/p&gt;

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

&lt;p&gt;When reliability becomes a primary concern, it is evident that API gateways are necessary as more organizations split their monoliths into microservices and move to cloud native architectures.&lt;/p&gt;

&lt;p&gt;However, this does not mean API gateways are for everyone. Depending on your API's size and usage, an API gateway might be overkill, and you can get away with using a reverse proxy with basic routing and load balancing capabilities.&lt;/p&gt;

&lt;p&gt;The use cases mentioned here only scratch the surface of an API gateway's capabilities. You can learn more about API gateways and Apache APISIX at &lt;a href="https://apisix.apache.org/" rel="noopener noreferrer"&gt;apisix.apache.org&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How and Why I Migrated My Blog From GitHub Pages to Netlify</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Thu, 25 Aug 2022 12:07:29 +0000</pubDate>
      <link>https://dev.to/pottekkat/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify-mkl</link>
      <guid>https://dev.to/pottekkat/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify-mkl</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  What GitHub Pages Lacked
&lt;/li&gt;
&lt;li&gt;  Enter Netlify
&lt;/li&gt;
&lt;li&gt;  How I Migrated to Netlify

&lt;ul&gt;
&lt;li&gt;  Create a Netlify Account
&lt;/li&gt;
&lt;li&gt;  Import Your Website
&lt;/li&gt;
&lt;li&gt;  Changing the Website URL and Custom Domains
&lt;/li&gt;
&lt;li&gt;  Configuring Deploy Previews
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;  Features I Might Add in the Future

&lt;ul&gt;
&lt;li&gt;  Form Handling
&lt;/li&gt;
&lt;li&gt;  Split Testing
&lt;/li&gt;
&lt;li&gt;  CDN/git LFS
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;  Too Good to be Free?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://navendu.me"&gt;My blog&lt;/a&gt; is now deployed on &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt; ! I spent a little more than an hour the other day migrating it from &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt; to Netlify.&lt;/p&gt;

&lt;p&gt;GitHub Pages is a perfect solution for deploying static websites. But, it made it challenging to implement some of the features I wanted on my blog.&lt;/p&gt;

&lt;p&gt;So, on impulse and to procrastinate from finishing a blog post, I migrated the site to Netlify!&lt;/p&gt;

&lt;h2&gt;
  
  
  What GitHub Pages Lacked
&lt;/h2&gt;

&lt;p&gt;GitHub Pages has been my go-to static website deployment solution for the past three years. And it worked like a charm even if it was free.&lt;/p&gt;

&lt;p&gt;I have been looking for ways to set up my Hugo website to show draft blog posts. The problem was that I didn't want these drafts to show up on the homepage listing, but I also wanted a sharable link for people to review.&lt;/p&gt;

&lt;p&gt;There are ways I can set this up, but even if I do that, there aren't any ways for people/reviewers to leave feedback on my static website.&lt;/p&gt;

&lt;p&gt;I also used a lot of client-side redirects, which is not desirable as opposed to server-side redirects. GitHub Pages did not provide a way for you to configure server-side redirects. So I had to use these &lt;a href="https://github.com/navendu-pottekkat/link-redirect"&gt;hacky, Jekyll redirects&lt;/a&gt; on a subdomain. It works, but it could be better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Netlify
&lt;/h2&gt;

&lt;p&gt;Netlify has &lt;a href="https://www.netlify.com/products/deploy-previews/"&gt;deploy previews&lt;/a&gt; . So, when you make a pull request to your production branch, Netlify will build the site for you and show a preview of what the change will look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A_X2Z9AS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/deploy-preview-is-ready.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A_X2Z9AS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/deploy-preview-is-ready.png%23center" alt="From github.com/navendu-pottekkat" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Deploy preview feature in Netlify&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;From &lt;a href="https://github.com/navendu-pottekkat/navendu-pottekkat.github.io/pull/23#issuecomment-1212009294"&gt;github.com/navendu-pottekkat&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You know what this is best for? Previewing draft blog posts!&lt;/p&gt;

&lt;p&gt;Using Netlify, I can open a pull request with my draft post, and Netlify will generate a preview build of the site without affecting my production site. Reviewers can see the preview and suggest edits as comments on GitHub.&lt;/p&gt;

&lt;p&gt;Netlify also brings a better continuous integration experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1OJffFfq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-ci-checks.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1OJffFfq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-ci-checks.png%23center" alt="From github.com/navendu-pottekkat" width="797" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CI checks run by Netlify&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;From &lt;a href="https://github.com/navendu-pottekkat/navendu-pottekkat.github.io/pull/23"&gt;github.com/navendu-pottekkat&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Netlify lets you configure the build settings through the Netlify UI or a &lt;a href="https://docs.netlify.com/configure-builds/file-based-configuration/"&gt;configuration file&lt;/a&gt; (&lt;code&gt;netlify.toml&lt;/code&gt;). This gives you a lot more control than the few configuration options GitHub Pages provides.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VjGh3SFA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-dashboard.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VjGh3SFA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-dashboard.png%23center" alt="I added a Plugin and to lint links and it broke the CI because there were a lot of broken links. Task for another day." width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Netlify dashboard for navendu.me&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I added a Plugin and to lint links and it broke the CI because there were a lot of broken links. Task for another day.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With Netlify, configuring redirects is as easy as adding two lines to your configuration file. You also have the option to configure the proper status code. Now that's neat.&lt;/p&gt;

&lt;p&gt;Netlify also has a DNS service, supports storing large media (Git LFS), has split testing and rollback features, and &lt;a href="https://www.netlify.com/github-pages-vs-netlify/#:~:text=Netlify%20is%20your%20all%2Din,Netlify%20is%20the%20obvious%20upgrade."&gt;more&lt;/a&gt; . But, these are only "nice to have" features for me now. &lt;em&gt;I will not be using these anytime soon&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I might use these sometime soon.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How I Migrated to Netlify
&lt;/h2&gt;

&lt;p&gt;The process was pretty straightforward. It only took me a little over an hour to set everything up and test.&lt;/p&gt;

&lt;p&gt;In the steps below, I have the following setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; blog published on GitHub Pages.&lt;/li&gt;
&lt;li&gt;  A custom domain registered on &lt;a href="https://www.hostgator.in/"&gt;Hostgator&lt;/a&gt; (also my DNS service).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can always refer to the &lt;a href="https://docs.netlify.com/get-started/"&gt;official Netlify docs&lt;/a&gt; if you have a different setup somewhere along this guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Netlify Account
&lt;/h3&gt;

&lt;p&gt;First, you must &lt;a href="https://app.netlify.com/signup"&gt;create a Netlify account&lt;/a&gt; if you don't have one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jsARLEBe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-signup.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jsARLEBe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-signup.png%23center" alt="Do you also use GitHub for everything?" width="510" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Create your Netlify account&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do you also use GitHub for everything?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Import Your Website
&lt;/h3&gt;

&lt;p&gt;You can now add your website to Netlify. You should have the code for your website on a Git provider. If you don't have one, now is the time to &lt;code&gt;git push&lt;/code&gt; your code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click on "Add new site" and "Import project".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--93xAR-cP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/import-existing-project.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--93xAR-cP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/import-existing-project.png%23center" alt="Importing your site" width="344" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Importing your site&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the Git provider where you have your website code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TMmTbBt4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/import-from-provider.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TMmTbBt4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/import-from-provider.png%23center" alt="Select your provider" width="773" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Select your provider&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pick the repository from the Git provider after granting access to Netlify.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hK8yT3xl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/picking-a-repository.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hK8yT3xl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/picking-a-repository.png%23center" alt="Pick the repository to deploy" width="784" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pick the repository to deploy&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can then configure the build settings based on your blog engine. Since I'm using Hugo, I will add the following configurations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TsKd9oQh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/build-settings.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TsKd9oQh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/build-settings.png%23center" alt="--gc cleans up old resources, --minify reduces the size of the files and the public folder is where Hugo outputs the build" width="707" height="946"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Build settings for my Hugo website&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--gc&lt;/code&gt; cleans up old resources, &lt;code&gt;--minify&lt;/code&gt; reduces the size of the files and the &lt;code&gt;public&lt;/code&gt; folder is where Hugo outputs the build&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And voila! Netlify will automatically build your first deployment. Now, you will be able to see the production URL for your website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ta7wc2uY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/generated-site-name.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ta7wc2uY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/generated-site-name.png%23center" alt="This screenshot is from Netlify&amp;amp;rsquo;s docs" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Production URL of your website&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This screenshot is from Netlify's docs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This URL is unique; you can change it to &lt;code&gt;yoursitename.netlify.app&lt;/code&gt; or your custom domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing the Website URL and Custom Domains
&lt;/h3&gt;

&lt;p&gt;You can change your site URL to anything unique. Once changed, this will be your website's address.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;From the "Site overview" page, Go to "Domain settings".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the "Options" next to the site name and click "Edit site name".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sHQeic3c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/edit-site-name.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sHQeic3c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/edit-site-name.png%23center" alt="kung-fu-panda-23 is a cool name" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit the generated site name&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;kung-fu-panda-23 is a cool name&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change the site name and save.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you use this URL, you might need to change the &lt;code&gt;baseURL&lt;/code&gt; in your Hugo configuration file to ensure all the links work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my setup, I'm using Hostgator as my DNS service and not the Netlify DNS service.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In "Domain settings", click "Add custom domain".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can enter the domain you already own or enter something new and purchase the domain. Netlify will set everything up if you are buying a domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you are using a domain you already own with an external DNS provider, you will see a warning. You can ignore that and click on "Add domain".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can also set up an SSL certificate from the HTTPS section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iWtp_VL2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/ssl-certificate.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iWtp_VL2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/ssl-certificate.png%23center" alt="Secure your site with an SSL certificate" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Secure your site with an SSL certificate&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you use a different DNS service, you need to configure it to point your domain to your Netlify website. You can check the &lt;a href="https://docs.netlify.com/domains-https/custom-domains/configure-external-dns/"&gt;Netlify docs&lt;/a&gt; if you have a different setup than mine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Login to your domain's control panel and open up your DNS configuration. I'm using Hostgator for my domain.&lt;/li&gt;
&lt;li&gt; Create an A Record with your apex domain and point it to Netlify's load balancer IP address &lt;code&gt;75.2.60.5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Then, create a CNAME record for the &lt;code&gt;www&lt;/code&gt;subdomain and point it to your website address &lt;code&gt;yoursitename.netlify.app&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, you must wait for the changes to propagate, and you will have your domain configured.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Deploy Previews
&lt;/h3&gt;

&lt;p&gt;The main reason to migrate was the deploy previews feature. And it is super easy to set up.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to "Site settings".&lt;/li&gt;
&lt;li&gt; Select "Build &amp;amp; deploy" from the side menu and then "Continuous Deployment".&lt;/li&gt;
&lt;li&gt; Scroll down to "Deploy previews" and set it up as desired. I have enabled it for any pull request against my production branch, and I have also enabled the Netlify Drawer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is it. You now have deploy previews!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: To ensure that the deploy previews show drafts, I updated my Netlify configuration file (&lt;code&gt;netlify.toml&lt;/code&gt;) to change the build command for deploy previews.&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[context.deploy-preview]
  command = "hugo --buildFuture --buildDrafts --gc --minify -b $DEPLOY_PRIME_URL"

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


&lt;p&gt;Here &lt;code&gt;$DEPLOY_PRIME_URL&lt;/code&gt; is an environment variable that Netlify sets, used to update the site's &lt;code&gt;baseURL&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That brings an end to my current setup. It is much better than my earlier setup with GitHub Pages and was pretty easy to migrate. I would definitely recommend Netlify for your static websites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features I Might Add in the Future
&lt;/h2&gt;

&lt;p&gt;This was my first iteration with Netlify. I have a basic setup that more or less does everything I need. But, I might use these other features if they are fruitful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Form Handling
&lt;/h3&gt;

&lt;p&gt;I use Mailchimp to handle subscriptions to this blog. If I can find a way to send mass emails, I might set up the free form handling offered by Netlify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Split Testing
&lt;/h3&gt;

&lt;p&gt;A/B test blog posts? Yes!&lt;/p&gt;

&lt;h3&gt;
  
  
  CDN/git LFS
&lt;/h3&gt;

&lt;p&gt;My blog contains a lot of images. I do my best to compress these images, but I will reach a point where the images are taking too much space, increasing the repo size. If Netlify's solution is better, I might switch to that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Too Good to be Free?
&lt;/h2&gt;

&lt;p&gt;Netlify seems too good to be free. I'm on the free tier, and it appears to be generous.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n5CVkCO1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-usage-navendu.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n5CVkCO1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://navendu.me/images/how-and-why-i-migrated-my-blog-from-github-pages-to-netlify/netlify-usage-navendu.png%23center" alt="Petition to add a section that shows the carbon footprint on my blog builds. Take that, people flying on private jets!" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My 24 hour Netlify usage&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Petition to add a section that shows the carbon footprint on my blog builds. Take that, people flying on private jets!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But, it will only be some time until I pass these limits and would end up needing to pay for the service. It is not too much money but seeing that the alternative, GitHub Pages, is free, I cannot stop thinking, "maybe I don't need deploy previews".&lt;/p&gt;

&lt;p&gt;I hope I don't have to write a post titled &lt;em&gt;"How and Why I Migrated My Blog Back From Netlify to GitHub Pages"!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>blog</category>
      <category>tutorial</category>
      <category>github</category>
    </item>
    <item>
      <title>How I Ask Questions as a Software Engineer</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Sat, 25 Dec 2021 16:19:17 +0000</pubDate>
      <link>https://dev.to/pottekkat/how-i-ask-questions-as-a-software-engineer-bp0</link>
      <guid>https://dev.to/pottekkat/how-i-ask-questions-as-a-software-engineer-bp0</guid>
      <description>&lt;p&gt;&lt;em&gt;Cover Photo by &lt;a href="https://unsplash.com/@nci?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;National Cancer Institute&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/indian-classroom?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I ask a lot of questions to my peers and to strangers on public forums in the internet. This year, I have been trying to improve this process to ask better questions. Here is how I do it.&lt;/p&gt;

&lt;p&gt;But first of all,&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Good Questions?
&lt;/h2&gt;

&lt;p&gt;Good questions are the ones which are easy to answer.&lt;/p&gt;

&lt;p&gt;Our goal for asking a question is to have the other person explain what they know in a way you can understand. A series of good questions is the key to a good answer.&lt;/p&gt;

&lt;p&gt;Bad:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;J: What happens when we strip the binaries? (Too vague and broad)&lt;/p&gt;

&lt;p&gt;N: Stripped binaries don’t have debugging information. So its size is reduced ... (Answers with a lot of irrelevant information)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;J: I see that we are stripping the binaries to reduce its size before publishing. I found that it shouldn’t affect the performance. Is that right? What other implications does this have? (Clear question, easy to answer)&lt;/p&gt;

&lt;p&gt;N: Stripping only removes the debugging information. It wouldn’t affect the performance in any way. But it will be difficult to debug if we run into any issues as debug symbols are removed from the traceback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem with Bad Questions
&lt;/h2&gt;

&lt;p&gt;Bad questions can derail a conversation easily.&lt;/p&gt;

&lt;p&gt;For me, asking bad questions have often resulted in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the person explaining things irrelevant to my question.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the person explaining things I have no clue of.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the person explaining what I already know.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the person not answering the question at all (especially for under researched questions).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this boils down to you or both of you walking away frustrated and without a clear answer.&lt;/p&gt;

&lt;p&gt;At this point it should be fairly obvious why you should focus on asking questions properly. So, here is my process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who are you Asking?
&lt;/h2&gt;

&lt;p&gt;Who you are asking a question should impact how you ask the question. Let me explain.&lt;/p&gt;

&lt;p&gt;If you are asking your coworker who works on your project or is familiar of the particular niche, you can fairly assume that the person has some context on what you are asking.&lt;/p&gt;

&lt;p&gt;This means that there would be less things for you to explain and you can build your explanation from your shared knowledge. But it is a different game when you are asking questions to the people of the interwebs.&lt;/p&gt;

&lt;p&gt;I have had my share of bashing from people in Stack Overflow when I began programming. I get that having a high bar for quality assurance helps Stack Overflow be the go to place to ask questions but some of the moderators are so trigger happy that they will shoot you (your question) down right away.&lt;/p&gt;

&lt;p&gt;But anyway, the important thing to remember here is that the person reading your question has very little context about your situation. It is obvious when a person has put very little effort in asking questions and these questions are the first to get the bashing.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Ask?
&lt;/h2&gt;

&lt;p&gt;If you have a lot of questions or if you think answering your question will take time, it is better to schedule a time when you are both available.&lt;/p&gt;

&lt;p&gt;If your questions are quick, it is better to ask them right away if it ends up saving you a lot of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Google First, Ask Later
&lt;/h2&gt;

&lt;p&gt;One of my biggest pet peeve is people who ask technical questions that can be answered by the first result of a Google search. It shows little effort from their part and now I just ask people to Google it and do not bother to answer until they do their homework.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I maintain a project called Meshery and one of the new contributors (who came in to get a &lt;a href="https://summerofcode.withgoogle.com/"&gt;GSoC internship&lt;/a&gt;) literally asked if I could explain what Meshery is.&lt;/p&gt;

&lt;p&gt;We have a website, 100+ pages of documentation, recordings of conference talks and technical documentation, all sent to the user as they join the community.&lt;/p&gt;

&lt;p&gt;You know how that conversation went.&lt;/p&gt;

&lt;p&gt;It would have been different if they had asked me something along the lines of “I have been going through Meshery’s docs and been trying it out locally. I’m not clear how Meshery adds value if a person is already using a service mesh. Could you point me to any docs where this is explained better?”.&lt;/p&gt;

&lt;p&gt;Think for a moment on how you would have answered in these scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Doing a bit of research can help you build some foundational knowledge to ask a set of better questions.&lt;/p&gt;

&lt;p&gt;The “Google first, ask later” motto is only good as a rule of thumb. Nothing has stopped me from asking obvious, googleable (&lt;a href="https://en.wiktionary.org/wiki/googleable"&gt;it is a real word&lt;/a&gt;) questions when in conversation with someone.&lt;/p&gt;

&lt;p&gt;To sum it up, take some effort, do your homework and then ask your questions. Don’t expect to be spoon-fed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is that Right?
&lt;/h2&gt;

&lt;p&gt;Let’s go back to the “stripped binary” example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;J: I see that we are stripping the binaries to reduce its size before publishing. I found that it shouldn’t affect the performance. Is that right? What other implications does this have?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See how stating what you already lets you build the rest of conversation?&lt;/p&gt;

&lt;p&gt;To ask this question, you have to spend some time and dig through what a stripped binary is and how it is different from a "normal" binary. The time taken to understand and formulate that question is time well spent.&lt;/p&gt;

&lt;p&gt;On the receiving end, the person will see that you have spent time in this and is not just asking them to do your work. It will also be easier to answer your question building by on your foundational knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;del&gt;Vague&lt;/del&gt; Precise Questions
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;J: How do I use a Kind cluster to setup my development environment?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you ask me this, I would reply with a link to the Kind docs. But this wasn’t what they intended to ask. So they say,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;J: I tried this but it is not working.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, there are million different reasons for this to not work. I am not Doctor Strange to evaluate all the possibilities in a second! A little bit more context might help.&lt;/p&gt;

&lt;p&gt;I will cut to the chase and say how I would ask this question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;N: I was trying to setup Kind for my local development environment. I am on macOS. I have Docker Desktop and Kind running. I have also setup Metallb LoadBalancer and I see the external IP of the service as shown on the logs below. Still, I am not able to reach it from my host machine. Is there something I’m missing?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then that senior engineer with years of experience can jump right in and say,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;S: On macOS, Docker does not expose the docker network to the host. You can try port-forwarding to reach the pods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See how easy it was to answer?&lt;/p&gt;

&lt;p&gt;This goes for all questions. The more precise you are with your questions, more easy it is to answer.&lt;/p&gt;

&lt;p&gt;This also prevents the person answering from going off on a tangent explaining irrelevant details which you may either not care about or aren’t relevant to your actual question.&lt;/p&gt;

&lt;p&gt;Another way to prevent shooting off on a tangent is to ask questions that can be answered by a simple yes/no.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;J: &lt;del&gt;Why are we using this gRPC middleware instead of directly calling the required service?&lt;/del&gt; &lt;/p&gt;

&lt;p&gt;J: Are we using this gRPC middleware to convert between two different configuration formats?&lt;/p&gt;

&lt;p&gt;N: Yes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The person usually goes to explain why yes/no after this but these questions are easy to answer and I almost always get quick responses.&lt;/p&gt;

&lt;p&gt;These questions are quite useful when you are in conversation with a person where they are explaining something to you. This segues into my next point.&lt;/p&gt;

&lt;h2&gt;
  
  
  When in Doubt, Ask More Questions
&lt;/h2&gt;

&lt;p&gt;Imposter syndrome is real.&lt;/p&gt;

&lt;p&gt;When I started working with other people, I often stopped myself from saying “I don’t understand” thinking I would look stupid.&lt;/p&gt;

&lt;p&gt;I have then come to learn that if you ask a “stupid” question, you are stupid for the day but if you don’t, you are stupid for life (because you will always stop yourself from asking questions, ending up not understanding things completely... umm, you get it right?).&lt;/p&gt;

&lt;p&gt;This means when you get an answer and you are not completely satisfied,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;say what you don’t understand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ask more clarifying questions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;stop the speaker and ask more specific questions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confronting the imposter syndrome is hard but it has been helpful to me in knowing that &lt;a href="https://www.ted.com/talks/elizabeth_cox_what_is_imposter_syndrome_and_how_can_you_combat_it"&gt;everyone else face this too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you start thinking “maybe I’m just not smart enough to understand the answer”, remember that people want to help you. You just have to help them help you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning in Public
&lt;/h2&gt;

&lt;p&gt;Ask questions in a public channel instead of DMs.&lt;/p&gt;

&lt;p&gt;This may not work in every situation but I try to do this more often now.&lt;/p&gt;

&lt;p&gt;This will document the discussions publicly and would also help any others looking in. You can then always point people to this discussion if they ask the same question.&lt;/p&gt;

&lt;p&gt;Take Stack Overflow for example. You almost always find answers to problems you are facing from questions asked by someone else.&lt;/p&gt;

&lt;p&gt;The imposter syndrome shifts to the next gear here. Face it head-on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asking Good Questions is a Skill
&lt;/h2&gt;

&lt;p&gt;And like all skills, it is sharpened with practice.&lt;/p&gt;

&lt;p&gt;Asking the right questions will help you extract the answers you want. In most scenarios, it is not that the person answering is incapable, but you are not asking the right questions.&lt;/p&gt;

&lt;p&gt;I have gotten better at this over the year and I am still working out the kinks in my process.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This might be a good post to come back in a year to reflect and improve upon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To summarise this post in a sentence,&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make it easy for people to help you.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Contributing to Documentation in Open-Source</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Mon, 08 Nov 2021 12:20:58 +0000</pubDate>
      <link>https://dev.to/pottekkat/contributing-to-documentation-in-open-source-h96</link>
      <guid>https://dev.to/pottekkat/contributing-to-documentation-in-open-source-h96</guid>
      <description>&lt;p&gt;Contributing to open-source can be overwhelming.&lt;/p&gt;

&lt;p&gt;You are creating a pull request to an open-source project that would open up your code for people to give feedback and criticise.&lt;/p&gt;

&lt;p&gt;This turns off a lot of new contributors from making an impact in open-source.&lt;/p&gt;

&lt;p&gt;You might also be at a point where you are not confident enough in your programming skills that you hesitate to take that first step in contributing code.&lt;/p&gt;

&lt;p&gt;A good way to tackle this and gain confidence to contribute code is to start by contributing to documentation.&lt;/p&gt;

&lt;p&gt;Documentation is necessary for every open-source project. Contributing to and maintaining documentation is not an easy task but is highly impactful.&lt;/p&gt;

&lt;p&gt;I will drop a bomb and say contributing to documentation is more important than contributing code.&lt;/p&gt;

&lt;p&gt;In this post, I will try to share my insights on contributing to documentation from my experience as an open-source contributor and a maintainer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Being overwhelmed when you first start to contribute to open-source is natural. Start small. But start.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Finding a Project
&lt;/h2&gt;

&lt;p&gt;The first thing to do before you can start contributing is finding a project you can contribute to. And the best project to contribute to is the one you have been using for a while.&lt;/p&gt;

&lt;p&gt;That is, if you have been using a framework or a library, a tool or any other open-source project, contribute to it.&lt;/p&gt;

&lt;p&gt;With this, you will have a lot of context on what the project is and you will definitely be able to find areas in the documentation you can improve.&lt;/p&gt;

&lt;p&gt;If you cannot find such a project to contribute to, look for projects with an active community of contributors.&lt;/p&gt;

&lt;p&gt;Community is key in open-source and you will definitely reap the rewards for being part of a thriving community.&lt;/p&gt;

&lt;p&gt;All these factors comes secondary to the fact that you should always contribute to projects that YOU are interested in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start as a User
&lt;/h2&gt;

&lt;p&gt;As mentioned in the above section, you will be able to make impactful contributions if you are already a user of the project.&lt;/p&gt;

&lt;p&gt;So, if you aren't, you should start by exploring the project from the perspective of a user.&lt;/p&gt;

&lt;p&gt;As a user, you will likely go through documentation and tutorials as you start out and you are likely to find bugs, out-dated content or things you can improve.&lt;/p&gt;

&lt;p&gt;When you find areas to improve, open up issues (or any form of tickets if you are not using GitHub) for these and discuss with a project maintainer to validate it and get it assigned to you.&lt;/p&gt;

&lt;p&gt;Once you have been assigned an issue, you can start contributing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the Contributing Guidelines
&lt;/h2&gt;

&lt;p&gt;Most (if not all) open-source projects will have a contributing guideline in their GitHub/GitLab repository.&lt;/p&gt;

&lt;p&gt;This document will be geared towards contributors with guidelines on setting up a developer environment and how you can make contributions.&lt;/p&gt;

&lt;p&gt;Read these guidelines carefully and make sure that you follow them.&lt;/p&gt;

&lt;p&gt;For example, if a project requires you to sign every commit, your pull requests will be rejected very quickly if you do not do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you Document?
&lt;/h2&gt;

&lt;p&gt;As mentioned in the previous sessions, you are likely to find issues when you start to use the project referring to the documentation.&lt;/p&gt;

&lt;p&gt;This could be as simple as an outdated screenshot to outdated or missing documentation.&lt;/p&gt;

&lt;p&gt;Open up issues for these as mentioned and discuss it with a maintainer to get it assigned to you.&lt;/p&gt;

&lt;p&gt;If you cannot find issues by yourself, you can try to fix already open issues.&lt;/p&gt;

&lt;p&gt;There are labels in GitHub issues that can help you filer out only "documentation" or "docs" issues and similar features will be there in any ticketing system being used by the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  In-Code Documentation
&lt;/h2&gt;

&lt;p&gt;People generally don't think of this when they think about documentation.&lt;/p&gt;

&lt;p&gt;In-code documentation refers to the error messages, help texts and other text the user interacts with that doesn't necessarily affect the "logic" of the code.&lt;/p&gt;

&lt;p&gt;These are really important as users will definitely interact with this while using the project.&lt;/p&gt;

&lt;p&gt;As a new contributor you have the magic eyes to spot issues with these that seasoned contributors are too close to notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Ways to go Beyond the Docs Page
&lt;/h2&gt;

&lt;p&gt;Good documentation is not just limited to the docs page.&lt;/p&gt;

&lt;p&gt;It can also involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing a blog post that takes the user through a new feature&lt;/li&gt;
&lt;li&gt;Creating a Twitter thread about the project&lt;/li&gt;
&lt;li&gt;Documenting processes that can be used for the community (a contributing guide for example)&lt;/li&gt;
&lt;li&gt;Updating the project's website&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can always ask the project maintainers to help you find areas that need contributions.&lt;/p&gt;

&lt;p&gt;Before we finish this, I want to point out that non-code open-source contributions are not just limited to writing documentation.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://dev.to/navendu/how-to-make-non-code-contributions-to-open-source-projects-35nj"&gt;check out other ways you can contribute to open-source projects without contributing code&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;div class="ltag__link__content"&gt;
    &lt;div class="missing"&gt;
      &lt;h2&gt;Article No Longer Available&lt;/h2&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://navendu.me/posts/contributing-to-documentation/"&gt;navendu.me&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Make Non-Code Contributions to Open-Source Projects</title>
      <dc:creator>Navendu Pottekkat</dc:creator>
      <pubDate>Sun, 19 Sep 2021 02:48:56 +0000</pubDate>
      <link>https://dev.to/pottekkat/how-to-make-non-code-contributions-to-open-source-projects-35nj</link>
      <guid>https://dev.to/pottekkat/how-to-make-non-code-contributions-to-open-source-projects-35nj</guid>
      <description>&lt;p&gt;Do you want to contribute to open-source but you are not sure of your coding skills?&lt;/p&gt;

&lt;p&gt;I have good news for you.&lt;/p&gt;

&lt;p&gt;Non-code contributions are more valuable than contributing code.&lt;/p&gt;

&lt;p&gt;But what are non-code contributions? How can I make non-code contributions? Why are you bashing coders?&lt;/p&gt;

&lt;p&gt;I will try to answer these questions in this post.&lt;/p&gt;

&lt;p&gt;And with &lt;a href="https://hacktoberfest.digitalocean.com/"&gt;Hacktoberfest&lt;/a&gt; right around the corner, I hope this post will encourage more open-source contributions.&lt;/p&gt;

&lt;p&gt;Like most of my posts, you can read this in Tweet format like the gods intended &lt;a href="https://twitter.com/sudo_navendu/status/1439233021496889356"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are non-code contributions?
&lt;/h2&gt;

&lt;p&gt;Any contribution that helps an open-source project that does not involve writing code. &lt;/p&gt;

&lt;p&gt;These can be anything but I have categorised it broadly into these categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✍️ Writing&lt;/li&gt;
&lt;li&gt;🎨 Designing&lt;/li&gt;
&lt;li&gt;🧑‍🔬 Testing/Using&lt;/li&gt;
&lt;li&gt;👥 Mentoring&lt;/li&gt;
&lt;li&gt;🧑‍🤝‍🧑 Community Managing/Organising&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's look at each of these categories in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. ✍️ Writing
&lt;/h2&gt;

&lt;p&gt;If you are good at technical writing and creating content, then this is for you.&lt;/p&gt;

&lt;p&gt;Here is how you can use your writing skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Write Documentation
&lt;/h3&gt;

&lt;p&gt;Contributing to documentation is really impactful.&lt;/p&gt;

&lt;p&gt;Even if the project is really amazing, without documentation, nobody would be able to figure out how to use it or at least would not get maximum value from the project.&lt;/p&gt;

&lt;p&gt;Here is a tip to help you contribute to docs:&lt;/p&gt;

&lt;p&gt;When you first check out an open-source project, go through their existing documentation and see if you can understand or use the project from what you see in the docs.&lt;/p&gt;

&lt;p&gt;Most likely, you will find areas that you can improve.&lt;/p&gt;

&lt;p&gt;Open issues for these and try to create a pull request adding changes that you think are needed.&lt;/p&gt;

&lt;p&gt;It is always helpful for projects to have a new pair of eyes look at things. New perspectives and new ideas often come from new contributors.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Write Tutorials and Blog Posts
&lt;/h3&gt;

&lt;p&gt;If you have been a user of the project and you are looking to contribute to it, then this is the best way.&lt;/p&gt;

&lt;p&gt;You can write tutorials and blog posts to help people who are using the project.&lt;/p&gt;

&lt;p&gt;You can also share your own insights and tips while writing these.&lt;/p&gt;

&lt;p&gt;If the project has a dedicated blog, you can ask to publish there or there are other platforms like this where you can freely publish content.&lt;/p&gt;

&lt;p&gt;I find myself looking for well-written and up-to-date tutorials and blogs instead of going through the documentation. &lt;/p&gt;

&lt;p&gt;There is no doubt that these will help the project as well as the users of the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.3 Translate Documentation
&lt;/h3&gt;

&lt;p&gt;People from different parts of the world could use your project.&lt;/p&gt;

&lt;p&gt;Language barrier is real and sometimes people may prefer Mandarin over English.&lt;/p&gt;

&lt;p&gt;So, if you know Mandarin and English or any other pair of languages, translate the documentation to the other language.&lt;/p&gt;

&lt;p&gt;This will open up the project to a whole new user base.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.4 Take Control of the Social Handles
&lt;/h3&gt;

&lt;p&gt;If your project has social media accounts, take control of it and start creating content.&lt;/p&gt;

&lt;p&gt;Even if you directly do not have access to the accounts, get in touch with the social media manager of the project and suggest content and ways to engage with your users and community.&lt;/p&gt;

&lt;p&gt;This will drive the project's visibility and will help gain more users and contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 🎨 Designing
&lt;/h2&gt;

&lt;p&gt;If you are a creative person and loves designing stuff, this is where you belong.&lt;/p&gt;

&lt;p&gt;Here is how you can use your palette to help the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Create Art!
&lt;/h3&gt;

&lt;p&gt;You can put your skills to use to create artworks for the project to share on social media, blog posts and even swags. (who doesn't like swags!)&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Create a Style Guide
&lt;/h3&gt;

&lt;p&gt;Most open-source projects have a large and a geographically distributed set of contributors.&lt;/p&gt;

&lt;p&gt;Among other challenges, this makes it difficult to maintain consistency in visual designs.&lt;/p&gt;

&lt;p&gt;As a designer and a contributor, you can create a style guide to ensure consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 🧑‍🔬 Testing/Using
&lt;/h2&gt;

&lt;p&gt;As a user of the open-source project, there are multiple ways in which you can make valuable contributions.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Reporting Bugs
&lt;/h3&gt;

&lt;p&gt;If you run into any bugs while using/testing the project, raise them.&lt;/p&gt;

&lt;p&gt;Open up an issue and let the code contributors know about the bug.&lt;/p&gt;

&lt;p&gt;This will help them identify and fix bugs improving the quality of the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Become and Advocate/Evangelist
&lt;/h3&gt;

&lt;p&gt;Most open-source projects are community-driven and they usually do not have a dedicated marketing team to publicise the project.&lt;/p&gt;

&lt;p&gt;This is where you step in as a volunteer advocate and talk about the project in events and social media while encouraging people to use the project.&lt;/p&gt;

&lt;p&gt;Here is a tip:&lt;/p&gt;

&lt;p&gt;Don't even ask the project owners before advocating for the project.&lt;/p&gt;

&lt;p&gt;Everyone likes free marketing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Help Improve the User Experience
&lt;/h3&gt;

&lt;p&gt;Most open-source project maintainers/code contributors are usually too close to the project to realise bad and unintuitive UX.&lt;/p&gt;

&lt;p&gt;I am and I know a lot of them.&lt;/p&gt;

&lt;p&gt;Step in here wearing your user hat and report these issues.&lt;/p&gt;

&lt;p&gt;They are always well received.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Sign Up for Alpha/Beta Testing
&lt;/h3&gt;

&lt;p&gt;Alpha/Beta tests are controlled tests of a new feature or a release to ensure quality and user experience before making it available to the general user base.&lt;/p&gt;

&lt;p&gt;As a user, you can sign up for the alpha/beta programs and test out the project before the new features are released and provide feedback.&lt;/p&gt;

&lt;p&gt;Feedback from these tests always provide insights that can help in iterating over the features/releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 👥 Mentoring
&lt;/h2&gt;

&lt;p&gt;Pay it forward by mentoring other contributors. &lt;/p&gt;

&lt;p&gt;Here are some of the ways in which you can do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Review Code
&lt;/h3&gt;

&lt;p&gt;The more people there is to review code, the better the quality of the code will be.&lt;/p&gt;

&lt;p&gt;Having more eyes on the code would mean less bugs, faster reviews and a better project.&lt;/p&gt;

&lt;p&gt;Most projects let anyone review pull requests and comment on it.&lt;/p&gt;

&lt;p&gt;If you have opinions, this is the place for you to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Mentor a Contributor
&lt;/h3&gt;

&lt;p&gt;Are you skilled in the project's tech stack?&lt;/p&gt;

&lt;p&gt;Can you help contributors make better contributions?&lt;/p&gt;

&lt;p&gt;Pay it forward by becoming a mentor!&lt;/p&gt;

&lt;p&gt;Share your experiences and knowledge and empower them to become a better open-source contributor.&lt;/p&gt;

&lt;p&gt;There are a lot of open-source programs which connect mentors and mentees.&lt;/p&gt;

&lt;p&gt;I had curated some of these in a previous article. You can check that out &lt;a href="https://dev.to/navendu/20-open-source-internship-programs-that-you-can-apply-to-37kh"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 🧑‍🤝‍🧑 Community Managing/Organising
&lt;/h2&gt;

&lt;p&gt;Open-source projects thrive as its community thrives. &lt;/p&gt;

&lt;p&gt;But who manages this community? Who manages the contributors? Who manages the project?&lt;/p&gt;

&lt;p&gt;This is where a community manager comes in.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Organise the Project
&lt;/h3&gt;

&lt;p&gt;Stale issues? Follow up!&lt;br&gt;
Issues without proper labels? Add them!&lt;br&gt;
Does this issue still exist? Verify and close them!&lt;br&gt;
Unclear issue descriptions? Ask for clarification!&lt;br&gt;
Unreviewed pull requests? Request for reviews!&lt;/p&gt;

&lt;p&gt;Help in every way you can to organise the project and make it whistle as a well oiled machine.&lt;/p&gt;

&lt;p&gt;From my experience, project organisers and community managers are well recognised in the community as well!&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Become the Release Manager
&lt;/h3&gt;

&lt;p&gt;A release manager keeps track of what everybody is working on and ensures that a project is ready for a release.&lt;/p&gt;

&lt;p&gt;Some of the responsibilities of a release manager are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checking with each team&lt;/li&gt;
&lt;li&gt;Ensuring different components and features are tested&lt;/li&gt;
&lt;li&gt;Organising the alpha/beta programs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This list can go on. Basically you decide when the project is ready to make a new release.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 Organise Events and Meetups
&lt;/h3&gt;

&lt;p&gt;Organise project meetings.&lt;br&gt;
Represent your project in conferences.&lt;br&gt;
Organise community events.&lt;/p&gt;

&lt;p&gt;This list is note exhaustive. &lt;/p&gt;

&lt;p&gt;Great community managers go above and beyond and are key players in the success of an open-source project.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.4 Get More Chefs to Cook (I mean, non-code contributors)
&lt;/h3&gt;

&lt;p&gt;Engineers attract other engineers.&lt;/p&gt;

&lt;p&gt;But the environment these engineers create are usually not super welcoming to non-code contributors.&lt;/p&gt;

&lt;p&gt;So, become a steward of the community and make it welcoming to newcomers!&lt;/p&gt;

&lt;p&gt;Host newcomer meetings, share onboarding resources, connect newcomers to mentors.&lt;/p&gt;

&lt;p&gt;What would we do without community managers?&lt;/p&gt;

&lt;p&gt;That's the list! Did I miss anything?&lt;/p&gt;

&lt;p&gt;Feel free to add them to the comments below.&lt;/p&gt;

&lt;p&gt;What other open-source related topic should I write about next?&lt;/p&gt;

&lt;p&gt;Comment below!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>codenewbie</category>
      <category>beginners</category>
      <category>hacktoberfest</category>
    </item>
  </channel>
</rss>
