Skip to content

Commit 778ea35

Browse files
committed
Initial commit
0 parents  commit 778ea35

10 files changed

+705
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
{
2+
"info": {
3+
"_postman_id": "404d4f90-c1b2-446c-85f1-5c464682e377",
4+
"name": "Gorilla mux RestAPI postgres",
5+
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
6+
},
7+
"item": [
8+
{
9+
"name": "Get all products",
10+
"request": {
11+
"method": "GET",
12+
"header": [],
13+
"url": {
14+
"raw": "http://localhost:8010/products",
15+
"protocol": "http",
16+
"host": [
17+
"localhost"
18+
],
19+
"port": "8010",
20+
"path": [
21+
"products"
22+
]
23+
}
24+
},
25+
"response": []
26+
},
27+
{
28+
"name": "Get one product",
29+
"request": {
30+
"method": "GET",
31+
"header": [],
32+
"url": {
33+
"raw": "http://localhost:8010/product/10",
34+
"protocol": "http",
35+
"host": [
36+
"localhost"
37+
],
38+
"port": "8010",
39+
"path": [
40+
"product",
41+
"10"
42+
]
43+
}
44+
},
45+
"response": []
46+
},
47+
{
48+
"name": "Delete one product",
49+
"request": {
50+
"method": "DELETE",
51+
"header": [],
52+
"url": {
53+
"raw": "http://localhost:8010/product/10",
54+
"protocol": "http",
55+
"host": [
56+
"localhost"
57+
],
58+
"port": "8010",
59+
"path": [
60+
"product",
61+
"10"
62+
]
63+
}
64+
},
65+
"response": []
66+
},
67+
{
68+
"name": "Add new product",
69+
"request": {
70+
"method": "POST",
71+
"header": [],
72+
"body": {
73+
"mode": "raw",
74+
"raw": "{ \"name\" : \"iPhone\", \"price\" : 1000 }"
75+
},
76+
"url": {
77+
"raw": "http://localhost:8010/product",
78+
"protocol": "http",
79+
"host": [
80+
"localhost"
81+
],
82+
"port": "8010",
83+
"path": [
84+
"product"
85+
]
86+
}
87+
},
88+
"response": []
89+
},
90+
{
91+
"name": "Edit existing product",
92+
"request": {
93+
"method": "PUT",
94+
"header": [],
95+
"body": {
96+
"mode": "raw",
97+
"raw": "{ \"name\" : \"iPhone\", \"price\" : 1001 }"
98+
},
99+
"url": {
100+
"raw": "http://localhost:8010/product/10",
101+
"protocol": "http",
102+
"host": [
103+
"localhost"
104+
],
105+
"port": "8010",
106+
"path": [
107+
"product",
108+
"10"
109+
]
110+
}
111+
},
112+
"response": []
113+
}
114+
]
115+
}

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Gorilla mux RestAPI postgres
2+
3+
REST API using Gorilla-mux in GO
4+
5+
1. Clone the git repo - `git clone [email protected]:golang-mitrah/Gorilla-mux-RestAPI-postgres.git`
6+
7+
2. Use `database/db_script.sql` file to crete a SQL table needed in your PostGres server for this demo project
8+
9+
3. Change database connection details in `database/database.go` file's line #27
10+
```
11+
var dbname, username, password, host, port = "DB_XX", "USER_XX", "PWD_XX", "localhost", "5432"
12+
```
13+
14+
4. Run below in your project path
15+
```
16+
go get .
17+
go run .
18+
```
19+
20+
5. Use the `Gorilla_mux_RestAPI_postgres.postman_collection.json` to import the API request into your [PostMan](https://www.postman.com/) tool to try out the API endpoints available
21+
22+
## **Contributors**
23+
24+
REST API using Gorilla MUX Router is authored by **[GoLang Mitrah](https://www.MitrahSoft.com/)** and everyone is welcome to contribute.
25+
26+
## **Problems**
27+
28+
If you experience any problems with REST API using Gorilla MUX please:
29+
30+
* [submit a ticket to our issue tracker](https://github.com/golang-mitrah/Gorilla-mux-RestAPI-postgres/issues)
31+
* fix the error yourself and send us a pull request
32+
33+
## **Social Media**
34+
35+
You'll find us on [Twitter](https://twitter.com/MitrahSoft) and [Facebook](http://www.facebook.com/MitrahSoft)

controllers/controllers.go

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package controllers
2+
3+
import (
4+
"database/sql"
5+
"encoding/json"
6+
"fmt"
7+
"log"
8+
"net/http"
9+
"os"
10+
"strconv"
11+
12+
"github.com/gorilla/mux"
13+
"github.com/jackc/pgx/v4"
14+
15+
"go_rest_api/models"
16+
)
17+
18+
func GetProducts(db *pgx.Conn, w http.ResponseWriter, r *http.Request) {
19+
// Converting from string into int
20+
count, _ := strconv.Atoi(r.FormValue("count"))
21+
start, _ := strconv.Atoi(r.FormValue("start"))
22+
23+
// If no count value given, set it as 10 by default
24+
if count <= 0 {
25+
count = 10
26+
}
27+
fmt.Printf("%d", count)
28+
fmt.Printf("%d", start)
29+
// If no start value given, set it as 0 by default
30+
if start < 0 {
31+
start = 0
32+
}
33+
34+
products, err := models.GetProducts(db, start, count)
35+
if err != nil {
36+
// If error, return as internal server error
37+
respondWithError(w, http.StatusInternalServerError, err.Error())
38+
return
39+
}
40+
41+
respondWithJSON(w, http.StatusOK, products)
42+
}
43+
44+
func GetProduct(db *pgx.Conn, w http.ResponseWriter, r *http.Request) {
45+
// Retrieve variables from the request payload
46+
vars := mux.Vars(r)
47+
48+
// Converting from string into int
49+
id, err := strconv.Atoi(vars["id"])
50+
if err != nil {
51+
// If error return with message
52+
respondWithError(w, http.StatusBadRequest, "Invalid product ID")
53+
return
54+
}
55+
56+
p := models.Product{ID: id}
57+
if err := p.GetProduct(db); err != nil {
58+
switch err {
59+
case sql.ErrNoRows:
60+
respondWithError(w, http.StatusNotFound, "Product not found")
61+
default:
62+
respondWithError(w, http.StatusInternalServerError, err.Error())
63+
}
64+
return
65+
}
66+
67+
respondWithJSON(w, http.StatusOK, p)
68+
}
69+
70+
func CreateProduct(db *pgx.Conn, w http.ResponseWriter, r *http.Request) {
71+
var p models.Product
72+
// Creating a log file to check
73+
file, err := os.OpenFile("gorilla-mux-restapi-postgres.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
74+
// Read data from the request payload ( input stream )
75+
decoder := json.NewDecoder(r.Body)
76+
77+
// Set the output file for logging
78+
log.SetOutput(file)
79+
80+
// Decoding the data and put into the product reference variable
81+
if err := decoder.Decode(&p); err != nil {
82+
// Logging if there is an err
83+
log.Fatal(err)
84+
respondWithError(w, http.StatusBadRequest, "Invalid payload")
85+
return
86+
}
87+
88+
// Delaying the execution and closing it when the need is over
89+
defer r.Body.Close()
90+
91+
if err != nil {
92+
log.Fatal(err)
93+
}
94+
95+
if err := p.CreateProduct(db); err != nil {
96+
respondWithError(w, http.StatusInternalServerError, err.Error())
97+
return
98+
}
99+
100+
respondWithJSON(w, http.StatusCreated, p)
101+
}
102+
103+
// This function is used to update a product by giving the unique identifier (ID)
104+
func UpdateProduct(db *pgx.Conn, w http.ResponseWriter, r *http.Request) {
105+
// Retrieve variables from the request payload
106+
vars := mux.Vars(r)
107+
108+
// Converting from string into int
109+
id, err := strconv.Atoi(vars["id"])
110+
if err != nil {
111+
respondWithError(w, http.StatusBadRequest, "Invalid product ID")
112+
return
113+
}
114+
115+
var p models.Product
116+
117+
// Read data from the request payload ( input stream )
118+
decoder := json.NewDecoder(r.Body)
119+
120+
// Decoding the data and put into the product reference variable
121+
if err := decoder.Decode(&p); err != nil {
122+
respondWithError(w, http.StatusBadRequest, "Invalid request payload")
123+
return
124+
}
125+
126+
// Delaying the execution and closing it when the need is over
127+
defer r.Body.Close()
128+
p.ID = id
129+
130+
if err := p.UpdateProduct(db); err != nil {
131+
respondWithError(w, http.StatusInternalServerError, err.Error())
132+
return
133+
}
134+
135+
respondWithJSON(w, http.StatusOK, p)
136+
}
137+
138+
// This function is used to delete a product by giving the unique identifier (ID)
139+
func DeleteProduct(db *pgx.Conn, w http.ResponseWriter, r *http.Request) {
140+
vars := mux.Vars(r)
141+
id, err := strconv.Atoi(vars["id"])
142+
if err != nil {
143+
respondWithError(w, http.StatusBadRequest, "Invalid Product ID")
144+
return
145+
}
146+
147+
p := models.Product{ID: id}
148+
if err := p.DeleteProduct(db); err != nil {
149+
respondWithError(w, http.StatusInternalServerError, err.Error())
150+
return
151+
}
152+
153+
respondWithJSON(w, http.StatusOK, map[string]string{"result": "success"})
154+
}
155+
156+
// This function is used to return the error with message in JSON format
157+
func respondWithError(w http.ResponseWriter, code int, message string) {
158+
// sending it as key-value pair using map
159+
respondWithJSON(w, code, map[string]string{"error": message})
160+
}
161+
162+
// This function is used to return the payload to the user in JSON format
163+
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
164+
// Converting the payload to JSON
165+
response, _ := json.Marshal(payload)
166+
167+
// Set the custom response header
168+
w.Header().Set("Content-Type", "application/json")
169+
w.WriteHeader(code)
170+
w.Write(response)
171+
}

database/database.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/url"
8+
9+
"github.com/gorilla/mux"
10+
"github.com/jackc/pgx/v4"
11+
)
12+
13+
// Initialize the App with router and DB
14+
type App struct {
15+
Router *mux.Router
16+
DB *pgx.Conn
17+
}
18+
19+
func InitializeApp() *App {
20+
app := new(App)
21+
app.ConnectToDB()
22+
return app
23+
}
24+
25+
// Make DB connection
26+
func (a *App) ConnectToDB() *pgx.Conn {
27+
var dbname, username, password, host, port = "gin_goorm_rest", "sm", "", "localhost", "5432"
28+
29+
// QueryEscape is used to escape the breaking special chars
30+
connectionString :=
31+
fmt.Sprintf("postgres://%s:%s@%s:%s/%s", url.QueryEscape(username), url.QueryEscape(password), url.QueryEscape(host), port, dbname)
32+
33+
var err error
34+
conn, err := pgx.Connect(context.Background(), connectionString)
35+
36+
a.DB = conn
37+
if err != nil {
38+
// log if error
39+
log.Fatal(err)
40+
}
41+
return conn
42+
}

database/db_script.sql

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
3+
CREATE SEQUENCE public.products_id_seq
4+
INCREMENT 1
5+
START 10
6+
MINVALUE 1
7+
MAXVALUE 9223372036854775807
8+
CACHE 1;
9+
10+
11+
CREATE TABLE IF NOT EXISTS public.products
12+
(
13+
id integer NOT NULL DEFAULT nextval('products_id_seq'::regclass),
14+
name text COLLATE pg_catalog."default",
15+
price numeric NOT NULL
16+
);

0 commit comments

Comments
 (0)