-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
220 lines (180 loc) · 7.43 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package main
import (
"fmt"
"go-fiber-postgres/models"
"go-fiber-postgres/storage"
"log"
"net/http"
"os"
"github.com/gofiber/fiber/v2"
"github.com/joho/godotenv"
"gorm.io/gorm"
)
// wil help to talk to database
type Repository struct {
DB *gorm.DB
}
// Decoding needed as golang itself doesn't understand json
type Book struct {
Author string `json:"author"`
Title string `json:"title"`
Publisher string `json:"publisher"`
}
// api contract
func (r *Repository) SetupRoutes(app *fiber.App) {
api := app.Group("/api")
api.Post("/create_books", r.CreateBook) //calling a method called createBook
api.Delete("/delete_book/:id", r.DeleteBook)
api.Get("/get_books/:id", r.GetBookByID)
api.Get("/books", r.GetBooks)
}
func (r *Repository) CreateBook(context *fiber.Ctx) error {
// Initialize an empty Book struct to hold incoming JSON data
book := Book{}
// Try to parse the JSON request body and map it to the book struct
// context.BodyParser automatically reads the body and unmarshals it into the provided struct
err := context.BodyParser(&book)
// If there is an error while parsing the request body, return a 422 Unprocessable Entity status
// and respond with an error message
if err != nil {
// Respond with 422 status code and error message if parsing fails
context.Status(http.StatusUnprocessableEntity).
JSON(&fiber.Map{"message": "request failed"})
return err
}
err = r.DB.Create(&book).Error
//If unable to create book in db
if err != nil {
context.Status(http.StatusBadRequest).JSON(&fiber.Map{
"message": "could not create book"})
return err
}
// If everything succeeds, return a 200 OK status and respond with a success message
context.Status(http.StatusOK).
JSON(&fiber.Map{"message": "book has been added"})
// Return nil indicating the function executed successfully and no error occurred
return nil
}
func (r *Repository) DeleteBook(context *fiber.Ctx) error {
// Initialize a pointer to an empty book model. This will be used to attempt deletion.
bookModel := &models.Books{}
// Retrieve the "id" parameter from the URL path.
id := context.Params("id")
fmt.Printf("mamata1 %v", id)
// Check if the "id" parameter is empty. If it is, return a 400 Bad Request response.
if id == "" {
context.Status(http.StatusBadRequest).JSON(&fiber.Map{
"message": "id cannot be empty", // Inform the client that the ID cannot be empty.
})
return nil // Return nil to end the function execution here.
}
// Attempt to delete the book with the specified ID from the database.
// The pointer to bookModel is passed to the Delete method to perform the deletion.
err := r.DB.Delete(bookModel, id).Error
// Check if there was an error during the delete operation.
if err != nil {
// If an error occurred, return a 500 Internal Server Error response with a message.
context.Status(http.StatusInternalServerError).JSON(&fiber.Map{
"message": "could not delete book", // General error message indicating failure to delete.
})
return err // Return the error to the caller for further handling.
}
// If no errors occurred, return a 200 OK response indicating successful deletion.
context.Status(http.StatusOK).JSON(&fiber.Map{
"message": "book deleted successfully", // Inform the client that the book was deleted successfully.
})
return nil // Return nil to indicate that the function completed successfully.
}
func (r *Repository) GetBookByID(context *fiber.Ctx) error {
// Initialize an empty book model to store the result of the query.
bookModel := &models.Books{}
// Retrieve the "id" parameter from the URL. This ID will be used to fetch the specific book.
id := context.Params("id")
// Check if the "id" parameter is empty. If it is, return a 400 Bad Request response.
if id == "" {
context.Status(http.StatusBadRequest).JSON(&fiber.Map{
"message": "id cannot be empty", // Provide a clear error message.
})
return nil // Return nil to indicate the end of the function.
}
// Attempt to fetch the book with the specified ID from the database.
// Use 'First' because we're expecting to retrieve a single record.
err := r.DB.First(bookModel, id).Error
// Check if there was an error during the query.
if err != nil {
// If the error indicates that no record was found, return a 404 Not Found response.
if err == gorm.ErrRecordNotFound {
context.Status(http.StatusNotFound).JSON(&fiber.Map{
"message": "book not found", // Inform the client that the book was not found.
})
} else {
// For any other types of errors (e.g., database connectivity issues), log the error
// and return a 500 Internal Server Error response.
log.Printf("Error fetching book: %v", err) // Log the error for debugging purposes.
context.Status(http.StatusInternalServerError).JSON(&fiber.Map{
"message": "could not get the book", // Provide a general error message.
})
}
return err // Return the error for potential further handling.
}
// If no errors occurred, return a 200 OK response with the fetched book data.
context.Status(http.StatusOK).JSON(&fiber.Map{
"message": "book fetched successfully", // Inform the client that the book was fetched successfully.
"data": bookModel, // Include the book data in the response.
})
return nil // Return nil to indicate successful completion of the function.
}
func (r *Repository) GetBooks(context *fiber.Ctx) error {
// Define a slice to hold the books
bookModels := []models.Books{}
/*
Why Slice is Best here:
Dynamic Size: Slices can grow and shrink as needed, making them perfect for querying a database where you don’t know how many records will be returned.
Sequential Data: Slices maintain the order of elements, which is often important when fetching data (e.g., sorting by title or published year).
Optimized for Iteration: Slices can be easily iterated over using Go's for loops, making it easy to process each book after fetching them.
ORM Compatibility: Go’s popular ORM libraries (e.g., GORM) expect a slice to populate multiple records, making it a natural fit.
*/
// Use the ORM to find all books and populate the bookModels slice
err := r.DB.Find(&bookModels).Error // Pass the slice as a pointer
// Check if there was an error during the database query
if err != nil {
// Log the detailed error internally (use a proper logging library in production)
fmt.Println("Error fetching books from database:", err.Error()) // Example of internal logging
// If there is an error, respond with a 400 Bad Request status and a generic error message
context.Status(http.StatusBadRequest).JSON(
&fiber.Map{"message": "could not get books"})
return err // Returning the error for internal handling (e.g., middleware)
}
// If successful, respond with a 200 OK status and the fetched books data
context.Status(http.StatusOK).JSON(&fiber.Map{
"message": "Fetched books successfully",
"data": bookModels,
})
return nil
}
func main() {
err := godotenv.Load(".env")
if err != nil {
log.Fatal(err)
}
config := &storage.Config{
Host: os.Getenv("DB_HOST"),
Port: os.Getenv("DB_PORT"),
DBName: os.Getenv("DB_NAME"),
SSLMode: os.Getenv("DB_SSLMode"),
}
db, err := storage.NewConnection(config) //from env
if err != nil {
log.Fatal("Could not load the database")
}
err = models.MigrateBooks(db)
if err != nil {
log.Fatal("could not migrate db")
}
r := Repository{
DB: db,
}
app := fiber.New() //kind of express
r.SetupRoutes(app) //struct methods
app.Listen(":8080") //start server on port
}