go
63 lines · 7 steps
A SQLite-backed order store in Go
A small repository type wraps database/sql to open a WAL-tuned SQLite connection and stream typed order rows.
Explained by
highlit
1package storage
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "time"
8
9 _ "github.com/mattn/go-sqlite3"
10)
11
12type Order struct {
13 ID int64
14 Customer string
15 Total float64
16 CreatedAt time.Time
17}
18
19type OrderStore struct {
20 db *sql.DB
21}
22
23func OpenOrderStore(path string) (*OrderStore, error) {
24 db, err := sql.Open("sqlite3", path+"?_journal_mode=WAL&_busy_timeout=5000")
25 if err != nil {
26 return nil, fmt.Errorf("open sqlite: %w", err)
27 }
28 db.SetMaxOpenConns(1)
29 if err := db.Ping(); err != nil {
30 db.Close()
31 return nil, fmt.Errorf("ping: %w", err)
32 }
33 return &OrderStore{db: db}, nil
34}
35
36func (s *OrderStore) RecentByCustomer(ctx context.Context, customer string, limit int) ([]Order, error) {
37 const query = `
38 SELECT id, customer, total, created_at
39 FROM orders
40 WHERE customer = ?
41 ORDER BY created_at DESC
42 LIMIT ?`
43
44 rows, err := s.db.QueryContext(ctx, query, customer, limit)
45 if err != nil {
46 return nil, fmt.Errorf("query orders: %w", err)
47 }
48 defer rows.Close()
49
50 var orders []Order
51 for rows.Next() {
52 var o Order
53 if err := rows.Scan(&o.ID, &o.Customer, &o.Total, &o.CreatedAt); err != nil {
54 return nil, fmt.Errorf("scan order: %w", err)
55 }
56 orders = append(orders, o)
57 }
58 return orders, rows.Err()
59}
60
61func (s *OrderStore) Close() error {
62 return s.db.Close()
63}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Wrapping a *sql.DB in a small struct gives callers typed methods instead of raw SQL scattered everywhere.
- 2Parameterized queries with ? placeholders keep user input out of the SQL string and prevent injection.
- 3Always defer rows.Close and check rows.Err so partial iteration failures aren't silently swallowed.
Related explainers
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
Intermediate
8 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
Intermediate
8 steps
go
package middleware import ( "net/http"
Per-plan export limits in Gin middleware
middleware
rate-limiting
authorization
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-sqlite-backed-order-store-in-go-explained-go-3f42/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.