Join our FREE personalized newsletter for news, trends, and insights that matter to everyone in America

Newsletter
New

Create An Api - Project Setup

Card image cap

In this new series we will be creating an API written in go, using a framework like Chi, connecting to a PostgreSQL, and have it deployed to a site like Railway.

The project will be hosted in GitHub

Requirements

In order to follow this tutorial, we will need to have the following installed:

  • The Go programing language, you can do this by following the instructions in the Go website
  • The PostgreSQL database, to install just follow the instructions in the PostgreSQL website
  • Git and GitHub for version control and remote repository
  • Railway Account so you will have a place to deploy the application

What are we going to be building

Through this tutorial, we will be creating an API which will be tracking a book store and store comments for each of the books.

We also will have a form of authentication, where we will sign in users, and with that we will implement a basic authorization module which will give some permissions to users.

We will be adding both unit and integration tests, so we can ensure that we are accomplishing each of the requirements of our MVP

Finally, each endpoint we will be creating should return the appropriate HTTP Code and the response will be using the JSON format

Setting up the project

  • Create the directory where we will be coding the application
mkdir bookstore 
cd bookstore 
  • Initialize Git for the project. It is my personal preference to add an empty commit as my initial commit
git init 
git commit --allow-empty -m "Intial Commit" 
  • Initialize the Go project
go mod init github.com/alcb1310/bookstore 

On this step we created the go.mod file and added the posibility to add libraries to our project

  • API entry point. Now we will start using best practices on where to place each file. Even though your main file could be placed in the project's root, it is a good practice to save it inside the /cmd/api directory
mkidr -p cmd/api 

Inside the /cmd/api folder we will create the main.go file as our application entry point

package main 
 
import fmt 
 
func main() { 
    fmt.Println("Hello bookstore") 
} 

To run the project, you can execute

go run ./cmd/api/main.go 

and the following will be displayed

Enabling Hot Reload

At the moment we have an entry point for our application, but we will need to stop and rerun manually each time we make changes in the application. There is a tool we can use and that one is Air, so let's setup it

  • Install Air in your system
go install github.com/air-verse/air@latest 

To make sure you've installed air in your system run

air -v 
  • Create a .air.toml configuration file, with this step we will setup some defaults for our project that will direct which file is your entry point
root = "." 
tmp_dir = "tmp" 
 
[build] 
# Just plain old shell command. You could use `make` as well. 
cmd = "go build -o ./build/main ./cmd/api/main.go" 
# Entrypoint binary relative to root. First item is the executable, more items are default arguments. 
entrypoint = ["./build/main"] 
delay = 1000 
# Ignore these filename extensions or directories. 
exclude_dir = ["build"] 
exclude_file = [] 
# Exclude specific regular expressions. 
exclude_regex = ["_test\\.go"] 
log = "build-errors.log" 
 
[color] 
# Customize each part's color. If no color found, use the raw app log. 
main = "magenta" 
watcher = "cyan" 
build = "yellow" 
runner = "green" 
 
[misc] 
clean_on_exit = true 
 
[screen] 
clear_on_rebuild = true 
keep_scroll = true 

Ignoring files for source contrl

As the final step in this article, there are files that we do not want to track its changes such as the binaries we will be generating, like the ones we will save in the build directory, we will need to create a .gitignore file for it

build 
tmp 

Adding our first route

Next step is to start using Chi, first we will need to add it to our project

go get -u github.com/go-chi/chi/v5 

It is a best practice to have our entry point as small as possible and have each process split in different modules, so following that, we will create a router module

mkidr -p internal/router 

The internal folder in Go makes that all the modules we create inside it, will not be visible from other projects, that is why we want to have our business logic inside it

internal/router/router.go

package router 
 
import ( 
    "fmt" 
    "log/slog" 
    "net/http" 
 
    "github.com/go-chi/chi/v5" 
) 
 
type service struct { 
    port uint16 
} 
 
func New(port uint16) *service { 
    return &service{ 
        port: port, 
    } 
} 
 
func (s *service) Router() error { 
    r := chi.NewRouter() 
 
    r.Get("/", func(w http.ResponseWriter, r *http.Request) { 
        w.Write([]byte("Hello world")) 
    }) 
 
    port := fmt.Sprintf(":%d", s.port) 
    slog.Info("Starting server", "port", port) 
    return http.ListenAndServe(port, r) 
} 

cmd/api/main.go

package main 
 
import "github.com/alcb1310/bookstore/internal/router" 
 
func main() { 
    var port uint16 = 8080 
 
    s := router.New(port) 
    if err := s.Router(); err != nil { 
        panic(err) 
    } 
} 

Reading from environment

Now we have our application working, but we have hardcoded the PORT in which the application will be listening to, to solve that we can use environment variables in a .env file at the root of our project.

To do so, we will first need to install a package

go get github.com/joho/godotenv 

With that we can change our main.go file

main.go

... 
import ( 
    "log/slog" 
    "os" 
    "strconv" 
    ... 
    _ "github.com/joho/godotenv/autoload" 
) 
 
func main() { 
    port64, err := strconv.ParseUint(os.Getenv("PORT"), 10, 16) 
    if err != nil { 
        slog.Error("Error parsing port", "error", err) 
        panic(err) 
    } 
    port := uint16(port64) 
 
    ... 
} 

Deploying the application

Now we have the application it its most basic form, so it is a good idea to deploy it.

  • First we need to push all of our code to GitHub
  • In railway home page, we click in the deploy button

  • We select GitHub Repository and select the repo you are using

  • Select the repo and go to the settings tab

  • In the Networking section, select Generate Domain, and select the default PORT 8080

After its done, click on the link provided and you should be able to see the message we added to our route, meaning our project is deployed and running, and every change we make in the main branch in GitHub will be deployed in railway

Deploying PR

Railway has a feature where you can automatically deploy PR's, it is always a good idea to enable them so you can test your changes before merging them, you can find that option in the project's settings page under the Environments tab