├── main.go ├── readme.md ├── utils.go ├── mysql.go ├── router.go ├── courses.go ├── categories.go ├── routes.go ├── tutorials.go ├── tags.go └── posts.go /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | router := NewRouter() 10 | log.Fatal(http.ListenAndServe(":9000", router)) 11 | } 12 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | TutorialEdge REST Api 2 | ======================= 3 | 4 | This is the REST API that currently powers the backend of https://tutorialedge.net 5 | 6 | ## Getting This Running 7 | 8 | ~~~ 9 | git clone 10 | go run *.go 11 | ~~~ 12 | 13 | 14 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | * Resp... the typical struct used to send back json responses 5 | */ 6 | type HttpResp struct { 7 | Status int `json:"status"` 8 | Description string `json:"description"` 9 | Body string `json:"body"` 10 | } 11 | -------------------------------------------------------------------------------- /mysql.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "log" 6 | ) 7 | 8 | func connect() *sql.DB { 9 | db, err := sql.Open("mysql", "root:charlie1@tcp(127.0.0.1:3306)/tuts") 10 | 11 | if err != nil { 12 | log.Fatal("Could not connect to database") 13 | } 14 | 15 | return db 16 | } 17 | -------------------------------------------------------------------------------- /router.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gorilla/mux" 4 | 5 | func NewRouter() *mux.Router { 6 | router := mux.NewRouter().StrictSlash(true) 7 | for _, route := range routes { 8 | router. 9 | Methods(route.Method). 10 | Path(route.Pattern). 11 | Name(route.Name). 12 | Handler(route.HandlerFunc) 13 | } 14 | return router 15 | } 16 | -------------------------------------------------------------------------------- /courses.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | ) 7 | 8 | /* 9 | * Course... a struct to represent a course in the database 10 | */ 11 | type Course struct { 12 | ID int `json:"id"` 13 | Title string `json:"title"` 14 | } 15 | 16 | func AllCourses(w http.ResponseWriter, r *http.Request) { 17 | db := connect() 18 | defer db.Close() 19 | 20 | var courses []Course 21 | results, err := db.Query("SELECT id, title FROM courses") 22 | 23 | for results.Next() { 24 | var course Course 25 | err = results.Scan(&course.ID, &course.Title) 26 | if err != nil { 27 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to retrieve all courses"}) 28 | } 29 | courses = append(courses, course) 30 | } 31 | 32 | json.NewEncoder(w).Encode(courses) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /categories.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | ) 9 | 10 | /* 11 | * Category... - a post struct for all my posts 12 | */ 13 | type Category struct { 14 | ID int `json:"id"` 15 | Name string `json:"name"` 16 | } 17 | 18 | func AllCategories(w http.ResponseWriter, r *http.Request) { 19 | fmt.Fprintln(w, "All Categories!") 20 | } 21 | 22 | func GetCategory(w http.ResponseWriter, r *http.Request) { 23 | vars := mux.Vars(r) 24 | categoryID := vars["id"] 25 | fmt.Fprintln(w, "Get Category:", categoryID) 26 | } 27 | 28 | func InsertCategory(w http.ResponseWriter, r *http.Request) { 29 | fmt.Fprintln(w, "Insert Category!") 30 | } 31 | 32 | func DeleteCategory(w http.ResponseWriter, r *http.Request) { 33 | fmt.Fprintln(w, "Delete Category!") 34 | } 35 | 36 | func EditCategory(w http.ResponseWriter, r *http.Request) { 37 | fmt.Fprintln(w, "Edit Category!") 38 | } 39 | -------------------------------------------------------------------------------- /routes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | type Route struct { 9 | Name string 10 | Method string 11 | Pattern string 12 | HandlerFunc http.HandlerFunc 13 | } 14 | type Routes []Route 15 | 16 | func Index(w http.ResponseWriter, r *http.Request) { 17 | fmt.Fprintln(w, "TutorialEdge REST API: v0.0.1") 18 | } 19 | 20 | var routes = Routes{ 21 | Route{"Index", "GET", "/", Index}, 22 | 23 | // All Tags Routes 24 | Route{"Alltags", "GET", "/tags", AllTags}, 25 | Route{"Gettag", "GET", "/tag/{id}", GetTag}, 26 | Route{"NewTag", "POST", "/tag", InsertTag}, 27 | Route{"EditTag", "POST", "/tag/{id}", EditTag}, 28 | Route{"DeleteTag", "DELETE", "/tag/{id}", DeleteTag}, 29 | // All Blog Posts Routes 30 | // ... 31 | Route{"AllPosts", "GET", "/posts", AllPosts}, 32 | Route{"GetPost", "GET", "/post/{id}", GetPost}, 33 | Route{"NewPost", "POST", "/post", InsertPost}, 34 | Route{"EditPost", "POST", "/post/{id}", EditPost}, 35 | Route{"DeletePost", "DELETE", "/post/{id}", AllPosts}, 36 | // All Tutorials Routes 37 | Route{"AllTutorials", "GET", "/tutorials", AllTutorials}, 38 | Route{"GetTutorial", "GET", "/tutorial/{id}", GetTutorial}, 39 | Route{"NewTutorial", "POST", "/tutorial", InsertTutorial}, 40 | Route{"EditTutorial", "POST", "/tutorial/{id}", EditTutorial}, 41 | Route{"DeleteTutorial", "DELETE", "/tutorial/{id}", AllTutorials}, 42 | // All Categories 43 | Route{"AllCategories", "GET", "/categories", AllCategories}, 44 | Route{"GetCategory", "GET", "/category/{id}", GetCategory}, 45 | Route{"NewCategory", "POST", "/category", InsertCategory}, 46 | Route{"EditCategory", "POST", "/category/{id}", EditCategory}, 47 | Route{"DeleteCategory", "DELETE", "/category/{id}", AllCategories}, 48 | // All Categories 49 | Route{"AllCourses", "GET", "/courses", AllCourses}, 50 | // Route{"GetCategory", "GET", "/category/{id}", GetCategory}, 51 | // Route{"NewCategory", "POST", "/category", InsertCategory}, 52 | // Route{"EditCategory", "POST", "/category/{id}", EditCategory}, 53 | // Route{"DeleteCategory", "DELETE", "/category/{id}", AllCategories}, 54 | } 55 | -------------------------------------------------------------------------------- /tutorials.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/gorilla/mux" 11 | ) 12 | 13 | /* 14 | * Tutorial... - standard tag used in frontend 15 | */ 16 | type Tutorial struct { 17 | ID int `json:"id"` 18 | Title string `json:"title"` 19 | Desc string `json:"description"` 20 | Body string `json:"body"` 21 | IsLive int `json:"isLive"` 22 | Author string `json:"author"` 23 | Slug string `json:"slug"` 24 | ImagePath string `json:"image_path"` 25 | } 26 | 27 | func AllTutorials(w http.ResponseWriter, r *http.Request) { 28 | db := connect() 29 | defer db.Close() 30 | 31 | var tutorials []Tutorial 32 | results, err := db.Query("SELECT id, title, description, body, slug, isLive, author, image_path FROM lessons") 33 | 34 | for results.Next() { 35 | var tutorial Tutorial 36 | err = results.Scan(&tutorial.ID, &tutorial.Title, &tutorial.Desc, &tutorial.Body, &tutorial.Slug, &tutorial.IsLive, &tutorial.Author, &tutorial.ImagePath) 37 | if err != nil { 38 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Failed to select all from tutorials"}) 39 | } 40 | tutorials = append(tutorials, tutorial) 41 | } 42 | 43 | json.NewEncoder(w).Encode(tutorials) 44 | } 45 | 46 | func GetTutorial(w http.ResponseWriter, r *http.Request) { 47 | vars := mux.Vars(r) 48 | tutorialID, err := strconv.Atoi(vars["id"]) 49 | if err != nil { 50 | fmt.Fprintln(w, "Not a Valid id") 51 | } 52 | // Open up our database connection. 53 | db := connect() 54 | defer db.Close() 55 | 56 | var tutorial Tutorial 57 | // Execute the query 58 | err = db.QueryRow("SELECT id, title, description, body, slug, isLive, author, image_path FROM lessons where id = ?", tutorialID).Scan(&tutorial.ID, &tutorial.Title, &tutorial.Desc, &tutorial.Body, &tutorial.Slug, &tutorial.IsLive, &tutorial.Author, &tutorial.ImagePath) 59 | if err != nil { 60 | log.Print(err.Error()) // proper error handling instead of panic in your app 61 | fmt.Fprintln(w, err.Error()) 62 | return 63 | } 64 | 65 | json.NewEncoder(w).Encode(tutorial) 66 | } 67 | 68 | func InsertTutorial(w http.ResponseWriter, r *http.Request) { 69 | fmt.Fprintln(w, "Insert Tutorial!") 70 | } 71 | 72 | func DeleteTutorial(w http.ResponseWriter, r *http.Request) { 73 | fmt.Fprintln(w, "Delete Tutorial!") 74 | } 75 | 76 | func EditTutorial(w http.ResponseWriter, r *http.Request) { 77 | fmt.Fprintln(w, "Edit Tutorial!") 78 | } 79 | -------------------------------------------------------------------------------- /tags.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "strconv" 9 | 10 | _ "github.com/go-sql-driver/mysql" 11 | "github.com/gorilla/mux" 12 | ) 13 | 14 | /* 15 | * Tag... - standard tag used in frontend 16 | */ 17 | type Tag struct { 18 | ID int `json:"id"` 19 | Name string `json:"name"` 20 | } 21 | 22 | func AllTags(w http.ResponseWriter, r *http.Request) { 23 | db := connect() 24 | defer db.Close() 25 | 26 | var tags []Tag 27 | results, err := db.Query("SELECT id, name FROM tags") 28 | 29 | for results.Next() { 30 | var tag Tag 31 | err = results.Scan(&tag.ID, &tag.Name) 32 | if err != nil { 33 | log.Print(err.Error()) // proper error handling instead of panic in your app 34 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Failed to select tag from database"}) 35 | } 36 | tags = append(tags, tag) 37 | } 38 | 39 | json.NewEncoder(w).Encode(tags) 40 | } 41 | 42 | func GetTag(w http.ResponseWriter, r *http.Request) { 43 | db := connect() 44 | defer db.Close() 45 | 46 | vars := mux.Vars(r) 47 | tagID, err := strconv.Atoi(vars["id"]) 48 | 49 | if err != nil { 50 | fmt.Fprintln(w, "Not a Valid id") 51 | } 52 | 53 | var tag Tag 54 | // Execute the query 55 | err = db.QueryRow("SELECT id, name FROM tags where id = ?", tagID).Scan(&tag.ID, &tag.Name) 56 | if err != nil { 57 | log.Print(err.Error()) // proper error handling instead of panic in your app 58 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to select tag from database"}) 59 | } 60 | 61 | json.NewEncoder(w).Encode(tag) 62 | } 63 | 64 | func InsertTag(w http.ResponseWriter, r *http.Request) { 65 | db := connect() 66 | defer db.Close() 67 | 68 | decoder := json.NewDecoder(r.Body) 69 | var tag Tag 70 | err := decoder.Decode(&tag) 71 | 72 | if err != nil { 73 | log.Print(err.Error()) 74 | } 75 | 76 | stmt, _ := db.Prepare("INSERT INTO tags (name) values (?)") 77 | res, err := stmt.Exec(tag.Name) 78 | if err != nil { 79 | log.Print(err.Error()) // proper error handling instead of panic in your app 80 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to select tag from database"}) 81 | } 82 | 83 | id, err := res.LastInsertId() 84 | if err != nil { 85 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to get last insert id"}) 86 | } 87 | 88 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Successfully Inserted Tag Into the Database", Body: strconv.Itoa(int(id))}) 89 | } 90 | 91 | func DeleteTag(w http.ResponseWriter, r *http.Request) { 92 | db := connect() 93 | defer db.Close() 94 | 95 | vars := mux.Vars(r) 96 | tagID := vars["id"] 97 | ID, _ := strconv.Atoi(tagID) 98 | 99 | stmt, err := db.Prepare("DELETE FROM tags where id = ?") 100 | if err != nil { 101 | log.Print(err.Error()) 102 | } 103 | 104 | _, err = stmt.Exec(ID) 105 | if err != nil { 106 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to delete tag from database"}) 107 | } 108 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Successfully Deleted Tag from the Database"}) 109 | 110 | } 111 | 112 | func EditTag(w http.ResponseWriter, r *http.Request) { 113 | db := connect() 114 | defer db.Close() 115 | 116 | decoder := json.NewDecoder(r.Body) 117 | var newTag Tag 118 | err := decoder.Decode(&newTag) 119 | 120 | vars := mux.Vars(r) 121 | tagID := vars["id"] 122 | ID, _ := strconv.Atoi(tagID) 123 | 124 | stmt, _ := db.Prepare("UPDATE tags SET name = ? WHERE id = ?") 125 | 126 | _, err = stmt.Exec(newTag.Name, ID) 127 | 128 | if err != nil { 129 | log.Print(err.Error()) 130 | } 131 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Successfully Update Tag in the Database"}) 132 | 133 | } 134 | -------------------------------------------------------------------------------- /posts.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/gorilla/mux" 11 | ) 12 | 13 | /* 14 | * Post... - a post struct for all my posts 15 | */ 16 | type Post struct { 17 | ID int `json:"id"` 18 | Title string `json:"title"` 19 | Desc string `json:"description"` 20 | Body string `json:"body"` 21 | IsLive int `json:"isLive"` 22 | Author string `json:"author"` 23 | Slug string `json:"slug"` 24 | } 25 | 26 | func AllPosts(w http.ResponseWriter, r *http.Request) { 27 | db := connect() 28 | defer db.Close() 29 | 30 | var posts []Post 31 | results, err := db.Query("SELECT id, title, description, body, slug, isLive, author FROM posts") 32 | 33 | for results.Next() { 34 | var post Post 35 | err = results.Scan(&post.ID, &post.Title, &post.Desc, &post.Body, &post.Slug, &post.IsLive, &post.Author) 36 | if err != nil { 37 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to select all from posts"}) 38 | } 39 | posts = append(posts, post) 40 | } 41 | 42 | json.NewEncoder(w).Encode(posts) 43 | } 44 | 45 | func GetPost(w http.ResponseWriter, r *http.Request) { 46 | db := connect() 47 | defer db.Close() 48 | 49 | vars := mux.Vars(r) 50 | postID, err := strconv.Atoi(vars["id"]) 51 | 52 | if err != nil { 53 | fmt.Fprintln(w, "Not a Valid id") 54 | } 55 | 56 | var post Post 57 | // Execute the query 58 | err = db.QueryRow("SELECT id, title, description, body, slug, isLive, author FROM posts where id = ?", postID).Scan(&post.ID, &post.Title, &post.Desc, &post.Body, &post.Slug, &post.IsLive, &post.Author) 59 | if err != nil { 60 | log.Print(err.Error()) // proper error handling instead of panic in your app 61 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to select tag from database"}) 62 | } 63 | 64 | json.NewEncoder(w).Encode(post) 65 | } 66 | 67 | func InsertPost(w http.ResponseWriter, r *http.Request) { 68 | db := connect() 69 | defer db.Close() 70 | 71 | decoder := json.NewDecoder(r.Body) 72 | var post Post 73 | err := decoder.Decode(&post) 74 | 75 | if err != nil { 76 | log.Print(err.Error()) 77 | } 78 | 79 | stmt, _ := db.Prepare("INSERT INTO posts (title, description, body, isLive, author, slug, created_at, updated_at, published_at) values (?,?,?,?,?,?, NOW(), NOW(), NOW()) ") 80 | res, err := stmt.Exec(post.Title, post.Desc, post.Body, post.IsLive, post.Author, post.Slug) 81 | if err != nil { 82 | log.Print(err.Error()) // proper error handling instead of panic in your app 83 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to insert post into database"}) 84 | } 85 | 86 | id, err := res.LastInsertId() 87 | if err != nil { 88 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to get last insert id"}) 89 | } 90 | 91 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Successfully Inserted Post Into the Database", Body: strconv.Itoa(int(id))}) 92 | 93 | } 94 | 95 | func DeletePost(w http.ResponseWriter, r *http.Request) { 96 | db := connect() 97 | defer db.Close() 98 | 99 | vars := mux.Vars(r) 100 | postID := vars["id"] 101 | ID, _ := strconv.Atoi(postID) 102 | 103 | stmt, err := db.Prepare("DELETE FROM posts where id = ?") 104 | if err != nil { 105 | log.Print(err.Error()) 106 | } 107 | 108 | _, err = stmt.Exec(ID) 109 | if err != nil { 110 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to delete post from database"}) 111 | } 112 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Successfully Deleted Post from the Database"}) 113 | 114 | } 115 | 116 | func EditPost(w http.ResponseWriter, r *http.Request) { 117 | db := connect() 118 | defer db.Close() 119 | 120 | decoder := json.NewDecoder(r.Body) 121 | var post Post 122 | err := decoder.Decode(&post) 123 | 124 | vars := mux.Vars(r) 125 | postID := vars["id"] 126 | ID, _ := strconv.Atoi(postID) 127 | 128 | stmt, _ := db.Prepare("UPDATE posts SET title = ?, description = ?, body = ?, author = ?, updated_at = NOW() WHERE id = ?") 129 | 130 | _, err = stmt.Exec(post.Title, post.Desc, post.Body, post.Author, ID) 131 | 132 | if err != nil { 133 | log.Print(err.Error()) 134 | json.NewEncoder(w).Encode(HttpResp{Status: 500, Description: "Failed to Update Post in the Database"}) 135 | } 136 | json.NewEncoder(w).Encode(HttpResp{Status: 200, Description: "Successfully Update Post in the Database"}) 137 | } 138 | --------------------------------------------------------------------------------