A Golang web application that allows posting job vacancies, managing candidate pipelines with a state machine, and visualizing statistics with graphs. Built with PostgreSQL and HTMX.
- Project Overview
- Setting up the Project
- Creating the Git Repository
- Implementing HTMX for Interactivity
- Database Schema and Migrations
- Writing Tests and Using Best Practices
- Setting Up Continuous Integration (CI)
- Deployment to Google Cloud Free Tier
- Releasing the First Version
- Best Practices Recap
- Congratulations
Job Vacancies Management: Create, update, delete, and list job vacancies. Candidate Pipeline Management: Track candidates through different stages using a state machine (e.g., Applied, Interviewing, Offered, Hired, Rejected). Statistics Visualization: Display graphs showing statistics like the number of applicants at each stage.
- Go 1.19 or later
- PostgreSQL
- Docker (for containerization)
- Google Cloud Account (for deployment)
Dynamic Content Loading: HTMX allows partial page updates without full reloads. Server-Driven Interactions: Simplifies the implementation of AJAX requests handled by the server. Enhanced User Experience: Provides a more responsive interface with less JavaScript code.
Data Storage: Stores job postings, candidate information, and pipeline data. Reliability: PostgreSQL is a robust, open-source relational database. Advanced Features: Supports complex queries and transactions.
- Project layout
your-project/
├── cmd/
│ └── app/
│ └── main.go
├── internal/
│ ├── handlers/
│ ├── models/
│ └── db/
├── pkg/
├── configs/
├── scripts/
├── test/
├── go.mod
├── go.sum
└── README.md
-
Package Organization:
- cmd/: Entry points of the application.
- internal/: Private application code.
- pkg/: Reusable packages.
- configs/: Configuration files.
- scripts/: Build and deploy scripts.
- test/: Test data and utilities.
-
Configuration Management:
- Use environment variables.
- Utilize packages like github.com/spf13/viper or github.com/kelseyhightower/envconfig.
Install Go
- Download and Install:
- Go to golang.org/dl and download the latest version.
- Follow the installation instructions for your operating system.
- Verify Installation:
go version
Install PostgreSQL
- MacOS:
brew install postgresql
- Ubuntu:
sudo apt-get update
sudo apt-get install postgresql postgresql-contrib
- Windows: Download the installer from postgresql.org.
Set Up HTMX
- No installation required; include it via CDN in your HTML templates:
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
- Initialize Go Modules:
go mod init github.com/yourusername/yourproject
- Add Dependencies:
- Import packages in your code, and Go will automatically add them to go.mod.
- Alternatively, use go get:
go get github.com/gorilla/mux
- Create a New Directory:
mkdir your-project
cd your-project
- Initialize Git:
git init
- Create a .gitignore File:
touch .gitignore
Add the following to .gitignore:
# Binaries
/bin/
/build/
*.exe
# Vendor directory
/vendor/
# Logs
*.log
# Environment variables
.env
# IDE files
.idea/
.vscode/
- Initial Commit:
git add .
git commit -m "Initial commit"
- Create a Repository on GitHub/GitLab (without README or .gitignore).
- Add Remote Origin:
git remote add origin https://github.com/yourusername/your-project.git
- Push to Remote:
git push -u origin master
<head>
<!-- Other head elements -->
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
</head>
Example: Adding a Candidate
<form hx-post="/candidates" hx-target="#candidate-list" hx-swap="beforeend">
<!-- Form fields -->
<button type="submit">Add Candidate</button>
</form>
func CreateCandidate(w http.ResponseWriter, r *http.Request) {
// Process form data
// Save to database
// Return a partial HTML snippet for the new candidate
}
-- Vacancies Table
CREATE TABLE vacancies (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
location VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Candidates Table
CREATE TABLE candidates (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
vacancy_id INTEGER REFERENCES vacancies(id),
state VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
#### Manage Migrations with `golang-migrate`
Install `golang-migrate`
- MacOS:
```bash
brew install golang-migrate
- Others: Download from golang-migrate GitHub.
mkdir -p db/migrations
migrate create -ext sql -dir db/migrations -seq create_tables
- xxxx_create_tables.up.sql: Place CREATE TABLE statements.
- xxxx_create_tables.down.sql: Place DROP TABLE statements.
migrate -database "postgres://user:password@localhost:5432/yourdb?sslmode=disable" -path db/migrations up
- Write Tests Early: Implement tests as you develop features.
- Use the
testing
Package: Standard Go testing tools. - Organize Tests: Place test files alongside code files with
_test.go
suffix. - Mock External Dependencies: Use interfaces to mock database interactions.
package handlers_test
import (
"net/http"
"net/http/httptest"
"testing"
"your-project/internal/handlers"
)
func TestListVacancies(t *testing.T) {
req, err := http.NewRequest("GET", "/vacancies", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(handlers.ListVacancies)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
}
// Additional assertions
}
go test ./...
Create Workflow File
Create .github/workflows/ci.yml
:
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '^1.19'
- name: Install dependencies
run: go mod download
- name: Run tests
run: go test ./...
- Sign Up: Create an account at cloud.google.com.
- Create a New Project: In the console, click on the project dropdown and select "New Project".
- Navigate to Cloud SQL: In the Google Cloud Console.
- Create Instance: 2.1 Choose PostgreSQL. 2.2 Set up instance ID, root password, and region.
- Configure Connectivity: 3.1 Enable public IP. 3.2 Set authorized networks to your IP for development.
- Create a Dockerfile:
# Use official Golang image as base
FROM golang:1.19-alpine
# Set the working directory
WORKDIR /app
# Copy go mod and sum files
COPY go.mod go.sum ./
# Download dependencies
RUN go mod download
# Copy the source code
COPY . .
# Build the application
RUN go build -o main ./cmd/app
# Expose port
EXPOSE 8080
# Start the application
CMD ["./main"]
- Build the Docker Image:
docker build -t gcr.io/your-project-id/your-app .
- Enable Cloud Run API:
gcloud services enable run.googleapis.com
- Deploy the Service:
gcloud run deploy your-app \
--image gcr.io/your-project-id/your-app \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--add-cloudsql-instances your-instance-connection-name \
--set-env-vars DB_CONNECTION_STRING="postgres://user:password@/dbname?host=/cloudsql/your-instance-connection-name&sslmode=disable"
- DB_CONNECTION_STRING: Use the Cloud SQL instance connection name.
- Other Configurations: Set any other necessary environment variables.
Update GitHub Actions Workflow
Add deployment step to ci.yml
:
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v0
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v0
- name: Deploy to Cloud Run
run: |
gcloud run deploy your-app \
--image gcr.io/your-project-id/your-app \
--region us-central1 \
--platform managed \
--allow-unauthenticated \
--add-cloudsql-instances your-instance-connection-name \
--set-env-vars DB_CONNECTION_STRING="postgres://user:password@/dbname?host=/cloudsql/your-instance-connection-name&sslmode=disable"
Ensure you have added GCP_CREDENTIALS
as a secret in your GitHub repository.
- Create a Git Tag:
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin v1.0.0
- Update Release Notes: In GitHub/GitLab, add release notes describing the features.
- Cloud Monitoring: Use to track performance metrics.
- Cloud Logging: Access logs to troubleshoot issues.
- Error Reporting: Receive alerts for application errors.
- Project Structure: Maintain a clean, standard Go project layout.
- Dependency Management: Use Go modules and pin dependency versions.
- Configuration Management: Use environment variables and configuration packages.
- Database Migrations: Use tools like golang-migrate to manage schema changes.
- Testing: Write unit and integration tests; aim for comprehensive coverage.
- CI/CD: Automate builds, testing, and deployments with tools like GitHub Actions.
- Security: Secure sensitive data and use best practices for authentication.
- Code Quality: Follow clean code principles; write readable and maintainable code.
- Documentation: Document your code and provide clear README files.
- Monitoring and Logging: Implement logging and monitoring for production environments.
You've built and deployed a Golang web application with PostgreSQL and HTMX, following industry best practices. Continue to iterate on your project, adding features, improving performance, and refining the user experience.