This tutorial will guide you through creating a CRUD (Create, Read, Update, Delete) API using Golang and PostgreSQL. We'll test all the REST APIs using Postman.
Prerequisites
- Go installed on your machine (latest version).
- PostgreSQL installed and running.
- Postman for testing APIs.
Step 1: Set Up Your Go Project
-
Create a new directory for your project and navigate into it:
mkdir go-crud-api-postgresql cd go-crud-api-postgresql
-
Initialize a new Go module:
go mod init go-crud-api-postgresql
Step 2: Install Required Packages
Install the necessary packages for handling PostgreSQL database and HTTP requests:
go get github.com/lib/pq
go get github.com/gorilla/mux
Step 3: Set Up PostgreSQL Database
-
Start PostgreSQL and create a new database:
CREATE DATABASE go_crud_api;
-
Create a new table:
\c go_crud_api; CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
Step 4: Create the Main Application File
Create a new file main.go
and set up the basic structure:
package main
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/gorilla/mux"
_ "github.com/lib/pq"
)
// User represents the model for our resource
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
var db *sql.DB
func main() {
// Initialize PostgreSQL database connection
var err error
connStr := "user=yourusername password=yourpassword dbname=go_crud_api sslmode=disable"
db, err = sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Create a new router
router := mux.NewRouter()
// Define endpoints
router.HandleFunc("/users", getUsers).Methods("GET")
router.HandleFunc("/user/{id}", getUser).Methods("GET")
router.HandleFunc("/user", createUser).Methods("POST")
router.HandleFunc("/user/{id}", updateUser).Methods("PUT")
router.HandleFunc("/user/{id}", deleteUser).Methods("DELETE")
// Start server
log.Fatal(http.ListenAndServe(":8000", router))
}
// getUsers fetches all users from the database
func getUsers(w http.ResponseWriter, r *http.Request) {
var users []User
rows, err := db.Query("SELECT id, name, email, created_at FROM users")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
for rows.Next() {
var user User
if err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
users = append(users, user)
}
json.NewEncoder(w).Encode(users)
}
// getUser fetches a single user by ID from the database
func getUser(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]
var user User
err := db.QueryRow("SELECT id, name, email, created_at FROM users WHERE id = $1", id).Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt)
if err != nil {
if err == sql.ErrNoRows {
http.NotFound(w, r)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
json.NewEncoder(w).Encode(user)
}
// createUser creates a new user in the database
func createUser(w http.ResponseWriter, r *http.Request) {
var user User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
err = db.QueryRow(
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, created_at",
user.Name, user.Email).Scan(&user.ID, &user.CreatedAt)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(user)
}
// updateUser updates an existing user in the database
func updateUser(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]
var user User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
_, err = db.Exec("UPDATE users SET name = $1, email = $2 WHERE id = $3", user.Name, user.Email, id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
user.ID = int(id)
json.NewEncoder(w).Encode(user)
}
// deleteUser deletes a user from the database
func deleteUser(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]
_, err := db.Exec("DELETE FROM users WHERE id = $1", id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}
Explanation of the Code and REST APIs
-
Package and Imports
- The
main
package is used, and necessary packages are imported. github.com/lib/pq
is the PostgreSQL driver.github.com/gorilla/mux
is used for routing.
- The
-
User Struct
- Represents the
User
model with JSON tags for marshalling/unmarshalling.
- Represents the
-
Global DB Variable
db
is a global variable to hold the database connection.
-
Main Function
- Initializes the PostgreSQL database connection.
- Creates a new router using
mux.NewRouter()
. - Defines the REST endpoints and associates them with handler functions.
- Starts the HTTP server on port 8000.
-
getUsers Function
- Retrieves all users from the
users
table. - Encodes the result as JSON and writes it to the response.
- Retrieves all users from the
-
getUser Function
- Retrieves a single user by ID.
- Returns a 404 error if the user is not found.
-
createUser Function
- Decodes the JSON request body to a
User
struct. - Inserts the new user into the
users
table. - Returns the created user as JSON.
- Decodes the JSON request body to a
-
updateUser Function
- Decodes the JSON request body to a
User
struct. - Updates the user with the specified ID in the
users
table. - Returns the updated user as JSON.
- Decodes the JSON request body to a
-
deleteUser Function
- Deletes the user with the specified ID from the
users
table. - Returns a 204 No Content status on successful deletion.
- Deletes the user with the specified ID from the
Testing with Postman
-
Get All Users
- Method:
GET
- URL:
http://localhost:8000/users
- Response: JSON array of users.
- Method:
-
Get a Single User
- Method:
GET
- URL:
http://localhost:8000/user/{id}
- Response: JSON object of the user.
- Method:
-
Create a New User
- Method:
POST
- URL:
http://localhost:8000/user
- Body:
{ "name": "John Doe", "email": "john@example.com" }
- Response: JSON object of the created user.
- Method:
-
Update an Existing User
- Method:
PUT
- URL:
http://localhost:8000/user/{id}
- Body:
{ "name": "Jane Doe", "email": "jane@example.com" }
- Response: JSON object of the updated user.
- Method:
-
Delete a User
- Method:
DELETE
- URL:
http://localhost:8000/user/{id}
- Response: 204 No Content status.
- Method:
Comments
Post a Comment