liveBook · Manning

Shortener

As explained in Go by Example

A shortener service is a service that converts URLs into shorter keys and resolves those keys back to URLs.

A shortener converts a URL into a compact key for sharing, then resolves that key back to the original URL. A REST API exposes the two operations: shorten a submitted link and redirect a request for its key. section 8.1

Figure 8.1 The Shorten and Resolve handlers provide a REST API over HTTP using the Shortener service, which is backed by an in-memory database.
A screenshot of a computer

Description automatically generated

Requests enter HTTP handlers, which delegate shortening and resolution to core link logic backed initially by in-memory storage. section 8.1

The core service stores Link values by Key. Shorten validates a link, supplies a key when needed, rejects duplicate keys, and saves the result. Resolve validates a key, looks it up, and returns a not-found error when absent. section 8.2.3

A practical implementation emphasizes reliable boundaries:

  • Keep core link logic independent of HTTP and storage packages, making it straightforward to test and reusable by multiple adapters. section 8.2
  • Make the zero value usable by lazily initializing the links map before its first write; use a constructor only when zero-value support would add complexity. section 8.2.3
  • Protect shared in-memory state for concurrent goroutines and use validation plus consistent wrapped errors for bad requests, conflicts, and missing links. section 8.2.3
  • Use matching operation names across layers, such as Resolve in both the HTTP package and service, so request flow remains easy to follow. section 8.1

In-memory service

Declare a Shortener with no setup, call Shorten to store a valid link, then call Resolve with its key. The service initializes its map on first storage; a missing key returns a not-found error rather than an empty link. section 8.2.3

Storage can change without changing the service role. A SQLite-backed implementation inserts links with ExecContext and resolves them with QueryRowContext and Scan, translating missing rows into the same not-found error used by other implementations. section 10.0 section 10.2.5

Listing 10.10 Resolving links (link/sqlite/shortener.go)
// Resolve resolves a [link.Link] by its [link.Key] from the database.
func (s *Shortener) Resolve(
    ctx context.Context, key link.Key,
) (link.Link, error) {
    if key.Empty() {
        return link.Link{}, fmt.Errorf(. . .)
    }
    if err := key.Validate(); err != nil {
        return link.Link{}, fmt.Errorf(
            "validating: %w: %w",
            err, link.ErrBadRequest,
        )
    }

    // Retrieve the link from the database.
    var uri string
    err := s.db.QueryRowContext(  #1
        ctx,
        `SELECT uri FROM links WHERE short_key = $1`,
        key,
    ).Scan(&uri) #2
    if errors.Is(err, sql.ErrNoRows) {  #3
        return link.Link{}, link.ErrNotFound
    }
    if err != nil {
        return link.Link{}, fmt.Errorf(
            "retrieving: %w: %w",
            err, link.ErrInternal,
        )
    }

    return link.Link{Key: key, URL: uri}, nil
}

The SQLite resolver validates the key, selects the URL by short key, scans the row, and maps an absent row to a service-level not-found error. section 10.2.5

How Go by Example covers it

How deep it goes
Implements map-backed shortening and resolution, zero-value initialization, validation, errors, concurrency safety, and a SQLite retrieval variant.
Examples
Uses POST shortening, GET resolution, a zero-value map-backed service, and SQLite QueryRowContext with Scan.
What you can do after
Build it from scratch as an in-memory URL shortener, then apply SQLite storage while preserving consistent resolution errors.
How it connects
Connects REST API handlers, HTTP redirects, Link, Key, Go maps, mutexes, SQLite, and consumer-driven interfaces.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go by Example. The section links point to the parts of the book it draws on.