Skip to content

fredrivett/here-now

Repository files navigation

here/now logo here/now — modern minimal webpage hit counter

Screenshot 2025-12-22 at 09 41 29

A minimal, self-hosted visitor tracking API that shows both total visitor count and real-time visitor counts per webpage.

Hosted original and example available at herenow.fyi.

✨ Features

  • Real-time visitor tracking - See current active visitors on a page
  • Total visitor counts - Track all-time unique visitors on a page
  • Self-hosted - Full control over your data
  • Lightweight widget - Single script tag integration
  • Dark/light theme detection - Automatic theme matching
  • SPA support - Works with React, Vue, Next.js, etc.
  • CORS enabled - Works from any website (domain filtering via allowlist)

Keeping a link to herenow.fyi in your implementation is appreciated but not required, as this helps others discover how to implement here/now.

🚀 Quick Start

Option A: Docker (Recommended for Self-Hosting)

The easiest way to self-host here/now is with Docker. This bundles everything you need (app + database) in one command.

Prerequisites: Docker installed on your machine.

# Clone the repository
git clone https://github.com/fredrivett/here-now.git
cd here-now

# Configure environment
cp .env.docker.example .env
# Edit .env and set:
#   POSTGRES_PASSWORD - any password (e.g., "herenow" for local, strong password for production)
#   ALLOWED_DOMAINS - leave as "localhost" for local development, or set to your domain(s) for production

# Start everything
docker compose up -d

# Your API is now running at http://localhost:3210

Deploying to production: The same Docker setup works on any server with Docker installed (e.g., DigitalOcean, AWS, your own VPS). Just clone, configure .env with a strong password and your domain, and run docker compose up -d.

Useful commands:

docker compose logs -f                  # View logs
docker compose down                     # Stop services
docker compose down -v                  # Stop and remove database data
docker compose up -d --build            # Rebuild after code changes
docker compose exec db psql -U herenow  # Access database directly

Option B: Node.js + External Database

Use this option if you want to:

  • Deploy to serverless platforms (Vercel, Netlify, Cloudflare Workers, etc.)
  • Use a managed database service (Supabase, Neon, Railway, etc.)
  • Run locally for development

1. Clone and Install

git clone https://github.com/fredrivett/here-now.git
cd here-now
npm install

2. Set up Database

You can use any database setup you choose, this guide works with Supabase (postgres).

Copy the environment variables:

cp .env.example .env

Set up your database:

  1. Create a free PostgreSQL database at supabase.com
  2. Go to Connect → Connection String and copy both connection strings
  3. Update .env with your Supabase URLs:
    DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-DB].supabase.co:6543/postgres?pgbouncer=true"  # Transaction Pooler
    DIRECT_URL="postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-DB].supabase.co:5432/postgres"  # Direct connection

Initialize database:

npm run db:generate  # Generates types
npm run db:push      # Creates tables in your database

3. Configure Domains

Add your allowed domains to .env:

ALLOWED_DOMAINS="localhost,yourdomain.com,yourotherdomain.com"

4. Start the Server

npm run dev

Your API will be available at http://localhost:3210

Add to Your Website

Once your server is running (via Docker or local development), add this single line to any webpage where you wish the widget to display:

<div data-herenow></div>

Then include the script before the closing </body> tag:

<script src="http://localhost:3210/widget.js" async></script>

Replace localhost:3210 with your production URL when deploying.

📁 Project Structure

here-now/
├── api/
│   └── index.ts                   # Vercel serverless entry point
├── src/
│   ├── app.ts                     # Express app configuration
│   ├── server.ts                  # Standalone server entry point
│   ├── controllers/               # Request handlers
│   │   ├── trackController.ts
│   │   ├── statsController.ts
│   │   └── widgetController.ts
│   ├── routes/                    # Route definitions
│   │   ├── track.ts
│   │   ├── stats.ts
│   │   └── widget.ts
│   ├── middleware/                # Custom middleware
│   │   └── cors.ts
│   ├── lib/                       # Utilities & external services
│   │   ├── prisma.ts
│   │   └── constants.ts
│   └── types/                     # TypeScript type definitions
│       └── index.ts
├── prisma/
│   └── schema.prisma              # Database schema
├── Dockerfile                     # Multi-stage Docker build config
├── docker-compose.yml             # Docker orchestration (app + database)
├── package.json
├── tsconfig.json
├── vercel.json                    # Vercel deployment config
├── .env.example                   # Environment variables for Node.js setup
└── .env.docker.example            # Environment variables for Docker setup

🔌 API Endpoints

The widget automatically calls these on page load so you don't need to implement them, but these are the API endpoints available:

Track Visitor

POST /api/track
Content-Type: application/json

{
  "domain": "yourdomain.com",
  "path": "/blog/post-1",
  "user_id": "optional-user-id",
  "session_id": "optional-session-id"
}

Get Stats

GET /api/stats?domain=yourdomain.com&path=/blog/post-1

Response:

{
  "here": 42,
  "now": 3,
  "domain": "yourdomain.com",
  "path": "/blog/post-1"
}

Widget Script

GET /widget.js

Returns the JavaScript widget code.

Health Check

GET /health

Returns { "status": "ok", "service": "here-now-api" } - used for health monitoring and Docker healthchecks.

☁️ Serverless Deployment

This project works great with serverless platforms. Here are some options:

Vercel (recommended for serverless)

The project includes Vercel configuration out of the box (vercel.json and api/index.ts).

  1. Push your code to GitHub
  2. Import the repo in Vercel
  3. Add environment variables (DATABASE_URL, DIRECT_URL, ALLOWED_DOMAINS)
  4. Deploy

Other Platforms

Netlify, Railway, Render: These platforms can run the Node.js server directly. Set environment variables and use npm run build && npm start as your start command.

Database options: Any PostgreSQL provider works - Supabase, Neon, Railway, or your own PostgreSQL instance.

⚙️ Environment Variables

Docker Setup

When using Docker (Option A), configure these in your .env file:

Variable Required Description
POSTGRES_PASSWORD Database password (any password for local, strong for production)
ALLOWED_DOMAINS Comma-separated list of allowed domains
API_BASE_URL Base URL for widget API calls (auto-detected from request)

Note: DATABASE_URL and DIRECT_URL are automatically configured by docker-compose.

Node.js Setup

When using Node.js with an external database (Option B), configure these in your .env file:

Variable Required Description
DATABASE_URL PostgreSQL connection string (with pgbouncer for pooling)
DIRECT_URL Direct database connection (for migrations)
ALLOWED_DOMAINS Comma-separated list of allowed domains
API_BASE_URL Base URL for widget API calls (auto-detected from request)

🤝 Contributing

Contributions welcome! Please read our contributing guidelines and submit pull requests.

📄 License

MIT License - see LICENSE file for details.

🔗 Links

About

Minimal webpage hit counter — show how many people are here/now

Resources

License

Contributing

Stars

Watchers

Forks

Contributors 2

  •  
  •