gator

command module
v0.0.0-...-f558d9f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: MIT Imports: 17 Imported by: 0

README

Gator

Go Version PostgreSQL

A terminal-based RSS feed aggregator written in Go. Gator lets multiple users add RSS feeds, follow feeds shared by others, continuously collect posts into PostgreSQL, and browse the latest articles without leaving the command line.

Table of Contents

Features

  • Multiple local users, each with their own followed feeds
  • Add RSS feeds and automatically follow them
  • Follow or unfollow feeds added by other users
  • Long-running aggregator that fetches the least recently updated feed on a timer
  • Posts stored in PostgreSQL and deduplicated by URL
  • Browse posts from the feeds you follow, newest first
  • Single self-contained binary after installation

Tech Stack

  • Go — CLI, HTTP fetching, and RSS/XML parsing
  • PostgreSQL — persistent storage
  • sqlc — type-safe database code generated from SQL
  • Goose — database migrations
  • lib/pq — PostgreSQL driver
  • google/uuid — UUID primary keys

Prerequisites

To build and run Gator you need:

macOS
brew install go
brew install postgresql@18
brew services start postgresql@18
Debian / Ubuntu / WSL

Install PostgreSQL with apt, and install Go from the official downloads page if your distribution's version is older than 1.27.3.

sudo apt update
sudo apt install postgresql postgresql-contrib
sudo service postgresql start

Verify both tools are available:

go version
psql --version

Gator compiles to a standalone binary. You need Go to install or build it, but you do not need the Go toolchain to run the resulting gator binary.

Installation

Install the CLI with go install:

go install github.com/3mmarx/gator@latest

Go places the binary in $(go env GOPATH)/bin. Make sure that directory is on your PATH:

export PATH="$(go env GOPATH)/bin:$PATH"

Add that line to your ~/.zshrc, ~/.bashrc, or equivalent shell profile to make it permanent.

Check the installation:

gator

Without arguments, Gator prints its usage message.

Build from source
git clone https://github.com/3mmarx/gator.git
cd gator
go build -o gator .

For a fully static binary:

CGO_ENABLED=0 go build -o gator .

Then run the binary directly:

./gator users

Database Setup

1. Create the database
createdb gator

If your PostgreSQL server requires a specific user or password, connect with psql and create the database there instead:

CREATE DATABASE gator;
2. Install Goose

Gator's schema is managed with Goose migrations located in sql/schema:

go install github.com/pressly/goose/v3/cmd/goose@latest
3. Apply the migrations

The migration files live in the repository, so run this from a clone of the project:

git clone https://github.com/3mmarx/gator.git
cd gator

goose -dir sql/schema postgres \
  "postgres://postgres:postgres@localhost:5432/gator?sslmode=disable" up

Replace the connection string with your own credentials. On macOS with Homebrew, your local PostgreSQL user is often your system username and has no password:

goose -dir sql/schema postgres \
  "postgres://$(whoami)@localhost:5432/gator?sslmode=disable" up

Useful migration commands:

# Show the status of every migration
goose -dir sql/schema postgres "<connection-string>" status

# Roll back the most recent migration
goose -dir sql/schema postgres "<connection-string>" down

Configuration

Gator reads its configuration from a JSON file in your home directory:

~/.gatorconfig.json

Create it before running any command:

{
  "db_url": "postgres://postgres:postgres@localhost:5432/gator?sslmode=disable",
  "current_user_name": ""
}
Field Description
db_url PostgreSQL connection string. The format is postgres://user:password@host:port/database?sslmode=disable.
current_user_name The user Gator acts as. gator register and gator login update this field automatically.

Examples for common local setups:

{
  "db_url": "postgres://ammar@localhost:5432/gator?sslmode=disable",
  "current_user_name": "ammar"
}
{
  "db_url": "postgres://postgres:postgres@localhost:5432/gator?sslmode=disable",
  "current_user_name": "postgres"
}

~/.gatorconfig.json stores database credentials. Do not commit it to version control. Gator does not create this file for you.

Quick Start

Register a user. This creates the account and makes it the current user:

gator register ammar

Add a few RSS feeds. Adding a feed also follows it automatically:

gator addfeed "Hacker News" "https://news.ycombinator.com/rss"
gator addfeed "Boot.dev Blog" "https://blog.boot.dev/index.xml"
gator addfeed "TechCrunch" "https://techcrunch.com/feed/"

Confirm what you are following:

gator following

Start collecting posts. This runs until you stop it with Ctrl+C, so leave it in its own terminal:

gator agg 1m

In another terminal, browse the latest posts from the feeds you follow:

gator browse 10

Without a limit, browse shows two posts:

gator browse

Commands

Command Description Requires login
gator register <username> Create a new user and set them as the current user No
gator login <username> Switch to an existing user No
gator users List all registered users; the current user is marked with * No
gator reset Delete all users and, by cascade, their feeds, follows, and posts No
gator feeds List every feed with its name, URL, and owner No
gator addfeed <name> <url> Add a feed and automatically follow it Yes
gator follow <url> Follow a feed that already exists Yes
gator following List the feeds followed by the current user Yes
gator unfollow <url> Unfollow a feed Yes
gator agg <duration> Continuously fetch the least recently fetched feed every <duration> No
gator browse [limit] Show recent posts from followed feeds; defaults to 2 Yes

Durations use Go syntax, for example 30s, 1m, or 1h. The agg command fetches feeds across all users, not just the current user.

Database Schema

erDiagram
    USERS ||--o{ FEEDS : owns
    USERS ||--o{ FEED_FOLLOWS : follows
    FEEDS ||--o{ FEED_FOLLOWS : "followed by"
    FEEDS ||--o{ POSTS : contains
  • users — registered accounts with unique names
  • feeds — RSS feeds, each owned by a user, with a nullable last_fetched_at
  • feed_follows — many-to-many follows between users and feeds
  • posts — fetched articles with a unique URL for deduplication

The full schema lives in sql/schema, and the queries used to generate the Go database layer live in sql/queries.

Development

Clone the repository and run Gator with go run:

git clone https://github.com/3mmarx/gator.git
cd gator

go run . register ammar
go run . addfeed "Hacker News" "https://news.ycombinator.com/rss"
go run . following

go run . is for development only. It compiles and runs the program on every invocation. For production, build a binary:

go build -o gator .
./gator browse 10
Regenerating the database layer

The internal/database package is generated by sqlc. After changing files in sql/queries or sql/schema, install sqlc and regenerate:

go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
sqlc generate
Checks
go build ./...
go vet ./...
Project structure
.
├── main.go                  # Entry point and command registration
├── commands.go              # Command type, registry, and dispatch
├── middleware_loggedin.go   # Resolves the current user for protected commands
├── rss_feed.go              # HTTP fetching and RSS/XML parsing
├── handler_*.go             # One handler per CLI command
├── internal/
│   ├── config/              # ~/.gatorconfig.json handling
│   └── database/            # sqlc-generated database code
├── sql/
│   ├── schema/              # Goose migrations
│   └── queries/             # SQL queries used by sqlc
├── sqlc.yaml
├── go.mod
└── go.sum

How It Works

  1. On startup, main.go reads ~/.gatorconfig.json, opens a PostgreSQL connection, and registers every command in a handler map.
  2. Commands that need a user are wrapped in middlewareLoggedIn, which loads the user named by current_user_name.
  3. gator agg selects the feed with the oldest last_fetched_at (or one that has never been fetched), marks it as fetched, downloads its RSS XML, and inserts each item as a post.
  4. Post URLs are unique, so running the aggregator repeatedly does not create duplicates.
  5. gator browse joins posts with feed_follows for the current user and returns the newest posts first.

Troubleshooting

Problem Fix
Failed to read the config file Create ~/.gatorconfig.json as described in Configuration.
connect: connection refused Start PostgreSQL, for example brew services start postgresql@18 or sudo service postgresql start.
pq: database "gator" does not exist Run createdb gator.
pq: relation "users" does not exist Apply the migrations with Goose.
zsh: command not found: gator Add $(go env GOPATH)/bin to your PATH.
failed to login with the user: user not found Register the user first with gator register <username>.
command not found from Gator Check the command name against the command reference.
no following feeds or no posts from browse Add or follow feeds, then run gator agg for a while.
duplicate key value violates unique constraint when adding a feed The URL is already registered. Use gator follow <url> instead.

Acknowledgements

Gator was built as part of the Build a Blog Aggregator in Go course on Boot.dev.

License

This project is licensed under the MIT License. See LICENSE for details.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址