DEV Community

Cover image for Golang image search api
Navin Kodag
Navin Kodag

Posted on

1 1

Golang image search api

Building the REST API

I'll be using gin for this part of project.

Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love Gin.

TLDR

First we create our go.mod file:

 go mod init Intersect_api
Enter fullscreen mode Exit fullscreen mode

Then we'll create our main.go file and add the following code.

// main.go
package main
import (
    "Intersect/server"
    "log"
    "os"
    "time"

    "github.com/getsentry/sentry-go"
)

func main() {

    // Initialize Libraries

    // Initialize and defer Sentry
    if err := sentry.Init(sentry.ClientOptions{
        Dsn: os.Getenv("SENTRY_DSN"),
    }); err != nil {
        log.Fatalln("Sentry Init Error: ", err)
    }

    defer sentry.Flush(2 * time.Second)

    // Initialize Server
    server.Init()
}
Enter fullscreen mode Exit fullscreen mode
  • Create a server module and initialize the server
/// server.go
package server

import "github.com/getsentry/sentry-go"

// Init : Initialize the routes and server
func Init() {
    r := NewRouter()
    err := r.Run()

    if err != nil {
        sentry.CaptureException(err)
    }
}

Enter fullscreen mode Exit fullscreen mode
  • Good rule of thumb to add some middleware with a token required
// middleware.go
package server

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func AuthMiddleware() gin.HandlerFunc {
    authHeader := "let-me-in"
    // os.Getenv("AUTHORIZATION_STRING")
    return func(c *gin.Context) {
        requiredAuth := c.Request.Header.Get("Authorization")
        if requiredAuth != authHeader {
            c.AbortWithStatusJSON(http.StatusUnauthorized, map[string]string{"message": "unauthorized peasant 😃"})
        }
        c.Next()
        // middleware
    }
}

Enter fullscreen mode Exit fullscreen mode
  • Then we'll create default cors, routes and also call our middleware inside the router.use() method
package server

import (
    "Intersect/scraper"

    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
)

/// router.go
func NewRouter() *gin.Engine {
    // gin.SetMode(gin.ReleaseMode)
    router := gin.New()
    // Gin and CORS Middlewares
    router.Use(gin.Logger())
    router.Use(gin.Recovery())
    /// Cors
    router.Use(setCors())
    /// Declare Middleware
    authorized := router.Group("/")
    authorized.Use(AuthMiddleware())
    {
        authorized.GET("", scraper.Greet)
        searchRouter := authorized.Group("search")
        searchRouter.GET("", scraper.GetImgs)
    }
    return router
}

// Cors
func setCors() gin.HandlerFunc {
    return cors.New(cors.Config{
        AllowOrigins:     []string{"*"},
        AllowMethods:     []string{"GET", "OPTIONS", "PUT"},
        AllowHeaders:     []string{"Origin", "Authorization"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
    })
}

Enter fullscreen mode Exit fullscreen mode

Then we'll create a scraper directory/module and create two files

  • greeting.go for default route and
  • search.go for the request route. (Because the default route will be lonely).
package scraper

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func Greet(c *gin.Context) {
    c.JSON(http.StatusOK, map[string]string{"sup": "🤘🚀"})

}

Enter fullscreen mode Exit fullscreen mode
package scraper

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
    "github.com/gocolly/colly"
)

func GetImgs(c *gin.Context) {
    searchQuery := c.Query("q")
    res := getSearch(searchQuery)
    c.JSON(http.StatusOK, res)
}

func getSearch(searchQuery string) Images {
    searchString := strings.Replace(searchQuery, " ", "-", -1)
    c := colly.NewCollector()
    c.UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0"
    c.AllowURLRevisit = true
    c.DisableCookies()
    array := []string{}

    // Find and visit all links
    c.OnHTML("img[src]", func(e *colly.HTMLElement) {
        src := e.Attr("src")
        if src != "" {
            array = append(array, e.Attr("src"))
        }
    })
    // Requesting a url for html
    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL)
    })
    // search query
    pexelsQuery := strings.Replace(searchString, "-", "%20", -1)
    stocSnapQuery := strings.Replace(searchString, "-", "+", -1)
    //
    c.Visit("https://unsplash.com/s/" + searchString)
    c.Visit("https://burst.shopify.com/photos/search?utf8=%E2%9C%93&q=" + searchString + "&button=")
    c.Visit("https://www.pexels.com/search/" + pexelsQuery + "/")
    c.Visit("https://www.flickr.com/search/?text=" + pexelsQuery)
    c.Visit("http://www.google.com/images?q=" + stocSnapQuery)
    c.Visit("https://stocksnap.io/search/" + stocSnapQuery)
    //
    return Images{
        Count: len(array),
        Data:  array}
}

type Images struct {
    Count int      `json:"counts"`
    Data  []string `json:"data"`
}

Enter fullscreen mode Exit fullscreen mode

After that, we'll build a binary using

go build -o /bin/Intersect_api -v .
Enter fullscreen mode Exit fullscreen mode

Now that's almost all the code we'll need.

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more