Image Search Abstraction Layer

FreeCodeCamp Backend Challenge 4

This microservice creates an abstraction layer (and history) between the user and the Google Images search API.

User Stories

  • User Story: I can get the image URLs, alt text and page urls for a set of images relating to a given search string.
  • User Story: I can paginate through the responses by adding a ?offset=2 parameter to the URL.
  • User Story: I can get a list of the most recently submitted search strings.

Code

 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
const https = require('https')
const imgSearchCollection = 'imgSearches'

// Challenge 4 - Image Search Abstraction Layer (search)
exports.query = function (req, res) {
  const resultsPerQuery = 10
  var localData = {
    searchTerm: '',
    pagination: 1,
  }
  if (!req.params.q) {
    res.json({'error': 'search query required'})
  } else {
    if (req.query.offset) {
      var offset_tmp = Number(req.query.offset)
      if (!isNaN(offset_tmp)) {
        localData.pagination = offset_tmp
      }
    }
    localData.searchTerm = req.params.q
    var options = {
      host: 'www.googleapis.com',
      port: 443,
      path: '/customsearch/v1?',
      method: 'GET',
      headers: {
        'Content-Type': 'application/json'
      }
    }
    options.path += 'searchType=image'
    options.path += '&safe=medium'
    options.path += '&fields=kind,items(title,link,snippet,image/contextLink,image/thumbnailLink)'
    options.path += '&key=' + process.env.G_SEARCH_API_KEY
    options.path += '&cx=' + process.env.G_CSE_ID
    options.path += '&q=' + localData.searchTerm
    options.path += '&start=' + Math.max(localData.pagination * resultsPerQuery, 1)

    const imgReq = https.request(options, function(imgRes) {
      var output = ''
      imgRes.setEncoding('utf8')

      imgRes.on('data', function (chunk) {
        output += chunk
      })

      imgRes.on('end', function () {
        localData.imgJSON = JSON.parse(output)

        var collection = req.app.dbConn.getDB().collection(imgSearchCollection)
        var lastDoc = collection.find().sort({ index: -1 }).limit(1)
        lastDoc.project({_id: 0, index: 1}).toArray(function (err, documents) {
          if (err) console.error(err)

          var insertIndex = 1
          if (documents.length > 0) {
            insertIndex += documents[0].index
          }

          collection.insertOne({
            index: insertIndex,
            query: localData.searchTerm
          }, function(err, r) {
            if (err) console.error(err)
            res.json(localData)
          })
        })
      })
    })

    imgReq.on('error', function (err) {
      res.send('error: ' + err.message)
    })

    imgReq.end()
  }
}

// Challenge 4 - Image Search Abstraction Layer (recent searches)
exports.latest = function (req, res) {
  var collection = req.app.dbConn.getDB().collection(imgSearchCollection)
  var lastSearches = collection.find().sort({ index: -1 }).limit(10)
  lastSearches.project({ _id: 0, query: 1 }).toArray(function (err, documents) {
    if (err) console.error(err.message)
    res.json(documents)
  })
}

Demo

View this code live on Heroku, usage:

Search for images by replacing {query} with your query, and paginate through results with {page}.

  • https://fcc-challenges.herokuapp.com/api/imagesearch/q/{query}?offset={page}

Show recent queries at the endpoint: