Start learning free

LESSON 68 OF 173 · BACKEND: APIS WITH A DATABASE

Search Products Safely

Practice

Clients often want to search instead of listing everything: /products?name=Mug. The part after the ? is the query string. r.URL.Query().Get("name") reads it, and gives the empty string "" when the request has no name.

Never build SQL by gluing a visitor's text into it. With "... WHERE name = '" + name + "'", the name x' OR '1'='1 turns the query into WHERE name = 'x' OR '1'='1', which is true for every row. That is SQL injection: the visitor's text becomes part of your SQL, and the same trick can read or change data they should never touch. Even an honest name like Ana's mug breaks the glued query.

A ? placeholder fixes it: the text travels separately, after the SQL string, and the database only ever compares it as a value. For example, on a route that searches a table books:

author := r.URL.Query().Get("author")
rows, _ := db.Query("SELECT title FROM books WHERE author = ?", author)
GET /products?name=Mug           →  200  [{"id": 1, "name": "Mug", "price": 12, "category": "kitchen", "stock": 10}]
GET /products?name=Gift card     →  200  [{"id": 8, "name": "Gift card", "price": 50, "category": null, "stock": 999}]
GET /products?name=x' OR '1'='1  →  200  []
GET /products?name=Ana's mug     →  200  []
GET /products                    →  200  [… all 8 products …]

Your task: let GET /products take an optional name. Without it, answer every product, as before. With it, answer only the products with exactly that name, passing the name through a ? placeholder.


Assignment
Follow the Coding Tutorial to read the name and run the right query for it.


Hint
Compare name with "" to tell a request without a name apart from one with a name. rows is declared above the if, so set it with =, not :=. In the search, pass name after the SQL string, never inside it.


STEP 1 OF 5
    Ctrl + Enter to check