A community-driven directory of little free libraries — those small book exchange spots found in public spaces where you can leave or take a book for free.
Book Corners lets anyone discover nearby little free libraries on an interactive map, submit new ones with photo and location, and report issues to keep the directory accurate.
Live site: bookcorners.org API docs: developers.bookcorners.org
- Interactive map — Browse all approved libraries on a full-page Leaflet map with marker clustering and popups
- Search and filtering — Full-text search across names and descriptions, filter by city/country/postal code
- Library submission — Upload a photo, pick a location on the map, and submit a new library for moderation
- EXIF GPS extraction — Photos with GPS metadata auto-populate the address fields via reverse geocoding
- Address autocomplete — Type-ahead suggestions powered by Photon (OpenStreetMap data)
- Photo optimization — Uploaded images are resized, compressed, and thumbnailed automatically
- Community reporting — Flag damaged, missing, or incorrectly listed libraries
- User authentication — Register with username/email or sign in with Google OAuth
- Admin moderation — Approve, reject, and manage submissions and reports through Django admin
- SEO — Sitemap, robots.txt, Open Graph tags on detail pages
- REST API — JWT-authenticated API for all core operations (list, search, submit, report)
| Layer | Technology |
|---|---|
| Backend | Python 3.14, Django 6, Django Ninja |
| Database | PostgreSQL 17 + PostGIS |
| Frontend | Django templates, HTMX, TailwindCSS 4 + daisyUI 5 |
| Maps | Leaflet + OpenStreetMap tiles |
| Geocoding | Nominatim (reverse) + Photon (autocomplete) |
| Auth | Session-based (web) + JWT (API), Google OAuth via django-allauth |
| Static files | WhiteNoise |
| Deployment | Dokku on Hetzner VPS, CI/CD via GitHub Actions |
- Docker and Docker Compose
- Node.js 22+ (for Tailwind CSS builds)
- Python 3.14
- uv (Python package installer)
The fastest way to get everything running:
# Clone the repository
git clone https://github.com/andreagrandi/book-corners.git
cd book-corners
# Copy environment template
cp .env.example .env
# Start the full stack (app + PostGIS + Tailwind watcher)
docker compose up app db tailwindThe app will be available at http://localhost:8000.
If you prefer running Django directly on your machine, you still need Docker for PostGIS:
# Install GIS libraries via Homebrew
brew install gdal geos proj
# Start only the database
docker compose up db -d
# Set up direnv (loads DATABASE_URL and GIS library paths from .envrc)
brew install direnv
eval "$(direnv hook zsh)" # or bash
direnv allow
# Create a virtual environment and install dependencies
python -m venv .venv
source .venv/bin/activate
uv pip install -r requirements.txt
# Install frontend dependencies and build CSS
npm ci
npm run build:css
# Apply migrations and start the dev server
python manage.py migrate
python manage.py runserverIn a separate terminal, start the Tailwind watcher for live CSS rebuilds:
npm run watch:cssPopulate the database with sample libraries for development:
# Local runtime
python manage.py seed_libraries --reset --count 36 --images-dir libraries_examples --seed 42
# Docker runtime
docker compose exec app python manage.py seed_libraries --reset --count 36 --images-dir libraries_examples --seed 42--resetdeletes existingReportandLibraryrows first--countcontrols how many libraries are generated--images-dirpoints to local seed images (images are reused automatically)--seedmakes generated data deterministic
If no images are found in the selected directory, the command generates placeholder images.
The libraries_examples/ directory is gitignored — each developer can use their own photos.
python manage.py createsuperuserAccess the admin at http://localhost:8000/admin/.
To enable "Continue with Google" locally:
- Create a project in Google Cloud Console
- Configure the OAuth consent screen (External, Testing mode)
- Create an OAuth 2.0 Client ID (Web application)
- Add
http://localhost:8000as an authorized origin - Add
http://localhost:8000/accounts/google/login/callback/as a redirect URI - Set
GOOGLE_OAUTH_CLIENT_IDandGOOGLE_OAUTH_CLIENT_SECRETin your.env
The Google sign-in button only appears when both variables are set.
Tests require a running PostGIS database:
# Start the database
docker compose up db -d
# Run the full test suite
nox -s tests
# Run a specific test
nox -s tests -- libraries/tests.py::TestLibraryModel::test_create_library_with_all_fields
# Run with verbose output
nox -s tests -- -vThe API is available at /api/v1/ with interactive documentation at /api/v1/docs.
Key endpoints:
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/v1/auth/register |
POST | No | Create account, returns JWT tokens |
/api/v1/auth/login |
POST | No | Authenticate, returns JWT tokens |
/api/v1/auth/refresh |
POST | No | Refresh an access token |
/api/v1/auth/me |
GET | JWT | Current user profile |
/api/v1/libraries |
GET | No | List and search libraries |
/api/v1/libraries/latest |
GET | No | Most recent approved libraries |
/api/v1/libraries/{slug} |
GET | No | Library detail |
/api/v1/libraries |
POST | JWT | Submit a new library |
/api/v1/libraries/{slug}/report |
POST | JWT | Report an issue |
Full API documentation: developers.bookcorners.org
book-corners/
├── assets/ # Frontend source (Tailwind CSS input)
├── config/ # Django project settings, URLs, API config
├── libraries/ # Core app: Library and Report models, views, API
├── users/ # Custom User model, auth views, JWT endpoints
├── templates/ # Django HTML templates
├── static/ # Generated CSS (gitignored)
├── media/ # User uploads (gitignored)
├── docs/ # API documentation source (Zensical/MkDocs)
├── scripts/ # Operational scripts (backup, restore)
├── docker-compose.yml # Local development services
├── Dockerfile # Multi-stage production build
├── Procfile # Dokku process definition
├── app.json # Dokku deployment hooks
├── noxfile.py # Test session configuration
├── requirements.txt # Python dependencies
└── package.json # Node.js / Tailwind dependencies
The developer documentation is built with Zensical and deployed to GitHub Pages.
# Build and preview docs locally
python manage.py export_openapi_schema > docs/openapi.json
zensical serveThe CI pipeline automatically regenerates the OpenAPI schema and deploys docs on every push to master that touches API or docs files.
Contributions are welcome. Here is how to get started:
- Fork the repository and clone your fork
- Follow the local development instructions above
- Create a feature branch from
master - Make your changes and add tests where appropriate
- Run the test suite to make sure everything passes:
nox -s tests - Open a pull request against
master
- Python: double quotes, type hints, descriptive test names
- Every function and test should have a two-line docstring (summary + intent)
- End files with a blank line, no trailing whitespace
Pull requests automatically run:
- The full test suite against PostGIS
- Tailwind CSS build verification
Pushes to master that pass CI are automatically deployed to production.
- HOSTING.md — Infrastructure overview, VPS setup, DNS, SSL, and environment configuration
- DEPLOYMENT.md — Deploy process, CI/CD pipeline, rollback procedures, and backups
This project is licensed under the MIT License.