DEV Community

Cover image for Create an API - Project Setup
Andres Court
Andres Court

Posted on

Create an API - Project Setup

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
Enter fullscreen mode Exit fullscreen mode
  • 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"
Enter fullscreen mode Exit fullscreen mode
  • Initialize the Go project
go mod init github.com/alcb1310/bookstore
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

To run the project, you can execute

go run ./cmd/api/main.go
Enter fullscreen mode Exit fullscreen mode

and the following will be displayed

First run

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
Enter fullscreen mode Exit fullscreen mode

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

air -v
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

    ...
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)