Building a Simple Web Server with Go
Tags: webdev, go, servers, programming
Published: 2023-08-15 14:31:45

Are you a beginner looking to learn about building a basic web server using the Go programming language? In this tutorial, we'll walk you through creating a straightforward web server that serves static files and handles basic HTTP requests. By the end of this tutorial, you'll have a solid understanding of the key concepts involved in building a web server with Go.
<h2 id="heading-what-is-a-web-server" class="permalink-heading"><strong>What is a Web Server?</strong></h2>
A web server is a software application that handles incoming requests from clients (typically web browsers) and responds by serving web content. When you type a URL into your web browser and hit Enter, your browser sends a request to a web server, which processes the request and sends back the appropriate response. This response can be a web page, an image, a file, or even dynamic content generated by the server.

Web servers play a crucial role in the architecture of the World Wide Web, as they facilitate the exchange of information between clients and servers. They are fundamental building blocks for serving websites, web applications, and other online services.
<h2 id="heading-prerequisites" class="permalink-heading">Prerequisites</h2>
Before you begin, make sure you have the following:
<ol>
 	<li>Basic understanding of Go programming concepts.
<blockquote>You can refer to this repo for Basics: <a href="https://github.com/ChetanThapliyal/get-started-with-Go/tree/main" target="_blank" rel="noopener">https://github.com/ChetanThapliyal/get-started-with-Go/tree/main</a>
OR
Read the Go blog Series: <a href="https://mechanical2devops.hashnode.dev/series/learn-golang" target="_blank" rel="noopener">https://mechanical2devops.hashnode.dev/series/learn-golang</a></blockquote>
</li>
 	<li>Go programming language installed on your machine. You can download and install Go from the official website: <a href="https://golang.org/dl/" target="_blank" rel="noopener">https://golang.org/dl/</a></li>
</ol>
<h2 id="heading-getting-started" class="permalink-heading">Getting Started</h2>
Let's start by creating a simple web server that serves static HTML files and handles HTTP requests. Follow these steps:
<h3 id="heading-step-1-setting-up-the-project" class="permalink-heading">Step 1: Setting Up the Project</h3>
<ol>
 	<li>Create a new directory for your project:
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<pre><code class="lang-sh"> mkdir WebServer-with-Go
 <span class="hljs-built_in">cd</span> WebServer-with-Go
</code></pre>
</div>
</div></li>
 	<li>Inside the project directory, create a subdirectory named <code>static</code>:
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<pre><code class="lang-sh"> mkdir static
</code></pre>
</div>
</div></li>
 	<li>Create two HTML files named <code>index.html</code> and <code>form.html</code> inside the <code>static</code> directory. You can use any text editor to create these files and add basic HTML content.
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<pre><code class="lang-bash"> <span class="hljs-built_in">cd</span> static
 touch index.html form.html hello.html
</code></pre>
</div>
</div>
<blockquote>Visit this to populate the above files (copy the code from the link mentioned below and paste in the above files):

<strong><mark>form.html</mark></strong> : <a href="https://github.com/ChetanThapliyal/WebServer-wih-Go/blob/main/static/form.html" target="_blank" rel="noopener">https://github.com/ChetanThapliyal/WebServer-wih-Go/blob/main/static/form.html</a>

<strong><mark>index.html</mark></strong>: <a href="https://github.com/ChetanThapliyal/WebServer-wih-Go/blob/main/static/index.html" target="_blank" rel="noopener">https://github.com/ChetanThapliyal/WebServer-wih-Go/blob/main/static/index.html</a>
<mark>hello.html</mark> : <a href="https://github.com/ChetanThapliyal/WebServer-wih-Go/blob/main/static/hello.html" target="_blank" rel="noopener">https://github.com/ChetanThapliyal/WebServer-wih-Go/blob/main/static/hello.html</a></blockquote>
</li>
</ol>
<ol>
 	<li>Use <code>tree</code> command to check directory structure:
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<pre><code class="lang-go"> tree
 WebServer-with-Go/
 ├── main.<span class="hljs-keyword">go</span>
 └── static
     ├── form.html
     ├── hello.html
     └── index.html
</code></pre>
</div>
</div></li>
</ol>
<h3 id="heading-step-2-writing-the-go-code" class="permalink-heading">Step 2: Writing the Go Code</h3>
<ol>
 	<li>Create a file named <code>main.go</code> in the project directory.</li>
 	<li>Open <code>main.go</code> in a text editor and copy the following code:
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<pre><code class="lang-go"> <span class="hljs-keyword">package</span> main

 <span class="hljs-keyword">import</span> (
     <span class="hljs-string">"fmt"</span>
     <span class="hljs-string">"log"</span>
     <span class="hljs-string">"net/http"</span>
 )

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">helloHandler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
     <span class="hljs-keyword">if</span> r.URL.Path != <span class="hljs-string">"/hello"</span> {
         http.Error(w, <span class="hljs-string">"404 Not Found"</span>, http.StatusNotFound)
         <span class="hljs-keyword">return</span>
     }
     <span class="hljs-keyword">if</span> r.Method != <span class="hljs-string">"GET"</span> {
         http.Error(w, <span class="hljs-string">"Method is not supported"</span>, http.StatusNotFound)
         <span class="hljs-keyword">return</span>
     }
     fmt.Fprintf(w, <span class="hljs-string">"Hello there"</span>)
 }

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">formHandler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
     <span class="hljs-keyword">if</span> err := r.ParseForm(); err != <span class="hljs-literal">nil</span> {
         fmt.Fprintf(w, <span class="hljs-string">"ParseForm() err: %v"</span>, err)
         <span class="hljs-keyword">return</span>
     }
     fmt.Fprintf(w, <span class="hljs-string">"POST Request Successful\n"</span>)
     name := r.FormValue(<span class="hljs-string">"name"</span>)
     address := r.FormValue(<span class="hljs-string">"address"</span>)
     fmt.Fprintf(w, <span class="hljs-string">"Name = %s\n"</span>, name)
     fmt.Fprintf(w, <span class="hljs-string">"Address = %s"</span>, address)
 }

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
     fileServer := http.FileServer(http.Dir(<span class="hljs-string">"./static"</span>))
     http.Handle(<span class="hljs-string">"/"</span>, fileServer)
     http.HandleFunc(<span class="hljs-string">"/form"</span>, formHandler)
     http.HandleFunc(<span class="hljs-string">"/hello"</span>, helloHandler)

     fmt.Printf(<span class="hljs-string">"Starting server at port 8080\n"</span>)

     <span class="hljs-keyword">if</span> err := http.ListenAndServe(<span class="hljs-string">":8080"</span>, <span class="hljs-literal">nil</span>); err != <span class="hljs-literal">nil</span> {
         log.Fatal(err)
     }
 }
</code></pre>
</div>
</div></li>
</ol>
<h3 id="heading-step-3-running-the-web-server" class="permalink-heading">Step 3: Running the Web Server</h3>
<ol>
 	<li>Open a terminal window and navigate to your project directory.</li>
 	<li>Run the following command to build and run the web server:
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<div>
<div><button class="absolute top-0 right-0 m-2 font-mono text-xs font-semibold uppercase text-white focus:outline-none"><span class="flex flex-row items-center leading-none"><span class="mr-1">COPY</span></span></button></div>
<pre><code class="lang-sh"> go run main.go
</code></pre>
</div>
</div></li>
 	<li>Open your web browser and visit <a href="http://localhost:8080/" target="_blank" rel="noopener">http://localhost:8080</a> to see your web server in action. You should be able to see the following:

<a href="http://localhost:8080/" target="_blank" rel="noopener">localhost:8080</a>
<a href="http://localhost:8080/hello.html" target="_blank" rel="noopener">localhost:8080/hello.html</a>
<a href="http://localhost:8080/form.html" target="_blank" rel="noopener">localhost:8080/form.html</a>

<img class="image--center mx-auto" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691737187612/24714052-15b7-4a87-8496-6aa18dcb3d2b.png?auto=compress,format&amp;format=webp" alt="" />

<img class="image--center mx-auto" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691737166404/de9a3d2e-e69a-4515-97d5-17a0bd1ff936.png?auto=compress,format&amp;format=webp" alt="" />

<img class="image--center mx-auto" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691737106805/c0e5cc30-7cd1-4fa1-9c9b-a44d0fb108cb.png?auto=compress,format&amp;format=webp" alt="" /></li>
</ol>
<h2 id="heading-general-understanding-of-the-code" class="permalink-heading">General Understanding of the Code</h2>
Let's break down the key components of the code you just created:
<ol>
 	<li><strong>Importing Packages</strong>: We import necessary packages like

<code>fmt</code>: Package for formatted I/O (used for printing messages).

<code>log</code>: Package for logging.

<code>net/http</code> Package for building HTTP servers &amp; for handling HTTP requests and responses.</li>
 	<li><strong>Handlers</strong>: We define two handler functions,

<code>helloHandler</code>: Handles requests to the "/hello" route. It checks if the URL path is "/hello" and if the HTTP method is "GET". If not, it returns a 404 error. If everything is in order, it responds with "Hello there".

<code>formHandler</code>: Handles POST requests to the "/form" route. It parses the form data from the request, extracts the values for the "name" and "address" fields, and then responds with a success message along with the extracted data.

to handle specific paths and HTTP methods.</li>
 	<li><strong>Serving Static Files</strong>: We create a file server to serve static files from the <code>static</code> directory using <code>http.FileServer</code>.</li>
 	<li><strong>Route Handling</strong>: We use <code>http.HandleFunc</code> to associate our handler functions with specific routes, such as <code>/hello</code> and <code>/form</code>.</li>
 	<li><strong>Listening and Serving</strong>: We use <code>http.ListenAndServe</code> to start the web server on port 8080. If an error occurs, we log it.</li>
</ol>
<h3 id="heading-detailed-understanding-of-functions" class="permalink-heading">Detailed Understanding of Functions</h3>
<h3 id="heading-hellohandler" class="permalink-heading"><code>helloHandler</code></h3>
<ol>
 	<li><code>func helloHandler(w http.ResponseWriter, r *http.Request)</code>: This line defines the function with two parameters:
<ul>
 	<li><code>w http.ResponseWriter</code>: This parameter allows you to write the HTTP response back to the client.</li>
 	<li><code>r *http.Request</code>: This parameter holds information about the incoming HTTP request.</li>
</ul>
</li>
 	<li><code>if r.URL.Path != "/hello" { ... }</code>: This line checks whether the URL path of the incoming request (<code>r.URL.Path</code>) is not equal to "/hello". If it's not, it means the request is not intended for the "/hello" route. In that case, the server responds with a "404 Not Found" error and returns early.</li>
 	<li><code>if r.Method != "GET" { ... }</code>: This line checks whether the HTTP method used in the request (<code>r.Method</code>) is not "GET". If the method is not "GET", it means that the "/hello" route only supports GET requests. If another method is used (like POST or PUT), the server responds with a "Method is not supported" error and returns early.</li>
 	<li><code>fmt.Fprintf(w, "Hello there")</code>: If the URL path is "/hello" and the HTTP method is "GET", this line writes the response "Hello there" to the <code>w</code> (ResponseWriter) parameter, which sends the response back to the client.</li>
</ol>
<h3 id="heading-formhandler" class="permalink-heading"><code>formHandler</code></h3>
<ol>
 	<li><code>if err := r.ParseForm(); err != nil { ... }</code>: This line parses the form data from the incoming request (<code>r</code>) and populates the <code>r.Form</code> map with the form values. If there's an error during parsing, it means the form data is not correctly structured, and the server responds with an error message indicating the parsing error.</li>
 	<li><code>fmt.Fprintf(w, "POST Request Successful\n")</code>: After successfully parsing the form data, the server writes "POST Request Successful" to the response. This indicates that the form submission was received and processed successfully.</li>
 	<li><code>name := r.FormValue("name")</code> and <code>address := r.FormValue("address")</code>: These lines extract the values of the "name" and "address" fields from the parsed form data. The <code>r.FormValue</code> function is used to retrieve the value associated with a specific form key.</li>
 	<li><code>fmt.Fprintf(w, "Name = %s\n", name)</code> and <code>fmt.Fprintf(w, "Address = %s", address)</code>: These lines write the extracted "name" and "address" values to the response. The <code>%s</code> placeholder is replaced with the actual values.</li>
</ol>
<h3 id="heading-main" class="permalink-heading"><code>main()</code></h3>
<ol>
 	<li><code>fileServer := http.FileServer(http.Dir("./static"))</code>: This line creates an HTTP file server (<code>fileServer</code>) that serves static files from the "./static" directory. The <code>http.Dir("./static")</code> part indicates the directory from which the files will be served.</li>
 	<li><code>http.Handle("/", fileServer)</code>: This line sets up a handler for the root ("/") route. Any request to the root route will be served by the <code>fileServer</code> created in the previous step, meaning that static files from the "./static" directory will be served when you access the root URL.</li>
 	<li><code>http.HandleFunc("/form", formHandler)</code>: This line sets up a handler for the "/form" route. When a request is made to the "/form" route, the <code>formHandler</code> function we discussed earlier will be called to handle the request.</li>
 	<li><code>http.HandleFunc("/hello", helloHandler)</code>: Similarly, this line sets up a handler for the "/hello" route. Requests to the "/hello" route will be handled by the <code>helloHandler</code> function.</li>
 	<li><code>fmt.Printf("Starting server at port 8080\n")</code>: This line prints a message indicating that the server is starting. You'll see this message in the console when you run the program.</li>
 	<li><code>if err := http.ListenAndServe(":8080", nil); err != nil { ... }</code>: This line starts the HTTP server on port 8080. The <code>http.ListenAndServe(":8080", nil)</code> function call does the following:
<ul>
 	<li>It listens for incoming HTTP requests on port 8080.</li>
 	<li>It uses the handlers you set up earlier (for "/", "/form", and "/hello") to route and handle the requests.</li>
</ul>
</li>
</ol>
If there's an error during server startup or while serving requests, the <code>log.Fatal(err)</code> line will print the error and terminate the program.
<h3 id="heading-usage" class="permalink-heading"><strong>Usage</strong></h3>
When this program is executed, it starts an HTTP server on port 8080. Here's what it does for different routes:
<ul>
 	<li>Visiting the root ("/") route serves static files from the "./static" directory.</li>
 	<li>Accessing the "/form" route displays a simple HTML form. Upon submitting the form, it processes the data and displays the "name" and "address" values.</li>
 	<li>Accessing the "/hello" route responds with "Hello there".</li>
</ul>
<h2 id="heading-conclusion" class="permalink-heading">Conclusion</h2>
Congratulations! You've successfully created a simple web server using the Go programming language. You've learned about serving static files, handling different HTTP requests, and setting up routes. This project provides a foundation for building more complex web applications and APIs using Go. Feel free to explore and expand upon this project to learn more about web development with Go.

Remember, practice makes perfect! Experiment with the code, add new features and explore the Go documentation to deepen your understanding of web development and the Go programming language. Happy coding!

This article was written by <a href="https://hashnode.com/@ChetanThapliyal">Chetan Thapliyal </a>