# Fullstack on Go Let's create a simple web application in the Go programming language, where you can leave and read quotes. To store them we will use the PostgreSQL DBMS. The application directory has the following structure: ``` code/ ├── static │ ├── styles.css │ ├── script.js │ └── index.html ├── amverum.yml ├── main.go ├── go.sum ├── go.mod └── Dockerfile ``` The code for the static files is available at the end of the page. ## Dockerfile ### Steps: 1. Create a Dockerfile in the project directory. 2. In the Dockerfile we specify the base image called *builder*: ```dockerfile FROM golang:1.21.1 AS builder ``` instead of 1.21.1 you can specify any other version that you need. 3. Setting the working directory: ```dockerfile WORKDIR /app ``` 4. Copy the files *main.go*, *go.mod* and *go.sum* to the current directory of the working directory: ```dockerfile COPY main.go go.mod go.sum ./ ``` 5. We build the project and create the executable file *server*: ```dockerfile RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o server ``` 6. We use the Alpine Linux image (this will reduce the size of the final image and improve its performance): ```dockerfile FROM alpine:latest ``` 7. Copy the *server* executable file, compiled in the previous image, to the current directory of the working directory: ```dockerfile COPY --from=builder /app/server ./ ``` 8. Copying static files inside the container: ```dockerfile COPY static/ ./static/ ``` 9. Open port 80 for external connections: ```dockerfile EXPOSE 80 ``` 10. Adding a command to launch the application: ```dockerfile CMD ["./server", "--port", "80"] ``` **The resulting Dockerfile:** ```dockerfile FROM golang:1.21.1 AS builder WORKDIR /app COPY main.go go.mod go.sum ./ RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o server FROM alpine:latest COPY --from=builder /app/server ./ COPY static/ ./static/ EXPOSE 80 CMD ["./server", "--port", "80"] ``` ## Amverum.yaml You can write a yaml file yourself or fill it out in the “Configuration” section of your personal account. **Example file amverum.yml:** ```yaml meta: environment: docker toolchain: docker build: dockerfile: Dockerfile skip: false run: persistenceMount: /data containerPort: "80" ``` ```{eval-rst} .. admonition:: Clue :class: hint If you use Dockerfile, then the amverum.yaml configuration file does not need to be added. ``` ## Dependencies (go.mod and sum.go) We initialize a new module for managing dependencies. To do this, run the following command, which will create the file *go.mod*: ```bash go mod init main ``` ```{eval-rst} .. admonition:: Clue :class: hint Instead of main, you can write an arbitrary line ``` All that remains is to run one more command, which will add two entries for each dependency and create a file *go.sum*: ```bash go mod tidy ``` ## DBMS deployment (PostgreSQL) The database needs to be deployed as a separate application, and then you can connect to it from the main application. Detailed instructions are available at [link](https://docs.amverum.ru/databases/postgreSQL.html#postgresql). ## Creating a project in Amverum The last step is to deploy the application itself. The *main.go* file contains the main code and connects to the database. Don't forget to change the parameters for connecting to the database to those that you used in the previous step when creating a database on Amverum: - **user** - Username - **password** - User password - **dbname** - Name of the database being created - the **host** parameter can be found on the *Info* page of your PostgreSQL project (for example, amverum-username-cnpg-appname-rw) **main.go:** ```golang package main import ( "database/sql" "encoding/json" "log" "net/http" "strconv" _ "github.com/lib/pq" ) // Specify the values that you specified when creating the database on Amverum Cloud const ( host = "amverum-nskripko-cnpg-godb-rw" port = 5432 user = "nick" password = "href239" dbname = "godb" ) func main() { portStr := strconv.Itoa(port) dbinfo := "host=" + host + " port=" + portStr + " user=" + user + " password=" + password + " dbname=" + dbname + " sslmode=disable" db, err := sql.Open("postgres", dbinfo) if err != nil { log.Fatal(err) } defer db.Close() _, err = db.Exec(`CREATE TABLE IF NOT EXISTS quotes ( id SERIAL PRIMARY KEY, quote TEXT NOT NULL )`) if err != nil { log.Fatal(err) } http.HandleFunc("/quotes", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } rows, err := db.Query("SELECT quote FROM quotes") if err != nil { log.Println("Error querying database:", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } defer rows.Close() var quotes []string for rows.Next() { var quote string if err := rows.Scan("e); err != nil { log.Println("Error scanning rows:", err) continue } quotes = append(quotes, quote) } json.NewEncoder(w).Encode(quotes) }) http.HandleFunc("/addquote", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var data struct { Quote string `json:"quote"` } if err := json.NewDecoder(r.Body).Decode(&data); err != nil { http.Error(w, "Bad request", http.StatusBadRequest) return } _, err := db.Exec("INSERT INTO quotes (quote) VALUES ($1)", data.Quote) if err != nil { log.Println("Error inserting quote into database:", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) }) fs := http.FileServer(http.Dir("static")) http.Handle("/", fs) log.Fatal(http.ListenAndServe(":80", nil)) } ``` ```{eval-rst} .. admonition:: Important :class: warning The code is a demo example and we strongly do not recommend specifying a login and password to connect to the database in code. Use environment variables (secrets)! ``` **To deploy your main application in Amverum you need to follow these simple steps:** 1. Open the page https://cloud.amverum.ru/projects 2. Click the *Create* button and select *service type* **application** 3. We upload all the files (you can use git, or you can use the interface). Make sure you have downloaded all the necessary files: - go.mod (necessarily) - go.sum (necessarily) - main.go (necessarily) - Dockerfile (necessarily) - static/index.html (if you use) - static/script.js (if you use) - static/styles.css (if you use - amverum.yml (necessarily) 4. After this, the [build](https://docs.amverum.ru/applications/build.html) and [deployment](https://docs.amverum.ru/applications/run.html) application will begin. Wait for the “Successfully Deployed” status to appear. ## Functionality check 1. Go to the project settings and activate the domain name:  2. Now you can go to this URL and our application will open:  If something does not work, we recommend that you read the Build and Application logs. Congratulations, you have successfully created your first application in Amverum! ## Static files code **static/index.html:** ```html