{"id":26613593,"url":"https://github.com/arraypress/applytics","last_synced_at":"2025-03-24T04:35:04.165Z","repository":{"id":281889240,"uuid":"945518804","full_name":"arraypress/Applytics","owner":"arraypress","description":"A lightweight, privacy-focused analytics platform for mobile apps built on Cloudflare Workers and D1","archived":false,"fork":false,"pushed_at":"2025-03-11T16:56:35.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-11T17:50:04.836Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arraypress.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-09T16:05:10.000Z","updated_at":"2025-03-11T16:56:40.000Z","dependencies_parsed_at":"2025-03-11T18:01:40.863Z","dependency_job_id":null,"html_url":"https://github.com/arraypress/Applytics","commit_stats":null,"previous_names":["arraypress/applytics"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2FApplytics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2FApplytics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2FApplytics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arraypress%2FApplytics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arraypress","download_url":"https://codeload.github.com/arraypress/Applytics/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245211925,"owners_count":20578437,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-24T04:35:03.547Z","updated_at":"2025-03-24T04:35:04.136Z","avatar_url":"https://github.com/arraypress.png","language":"JavaScript","readme":"# Cloudflare Applytics\n\nA lightweight, privacy-focused analytics tracking and reporting system built on Cloudflare Workers and D1.\n\n## Privacy-First Analytics\n\nCloudflare Applytics is designed to be a privacy-aware tracking solution for app developers who need minimal metrics like app installs, purchases, and user engagement without compromising user privacy.\n\n**Privacy features:**\n- No personal information tracking\n- No cookies or persistent identifiers\n- No cross-site tracking\n- No IP address storage\n- Fully compliant with privacy regulations\n- Data stored in your own Cloudflare account\n\nThis solution is ideal for developers who want basic app metrics without becoming entangled in complex privacy requirements or third-party analytics that might compromise user data.\n\n## Features\n\n- Simple API key authentication\n- Track single events or batches\n- Category-based event organization\n- Cumulative stats with filtering options\n- Timeseries data with custom periods\n- Multiple metric comparison\n- Event history and detailed reporting\n- Dashboard with trending metrics\n\n## Use Cases\n\n### App Lifecycle Events\n\nTrack critical milestones in your app's lifecycle:\n\n```bash\n# Track app installation\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"install\", \"value\": 1, \"category\": \"lifecycle\"}'\n\n# Track app update\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"update\", \"qualifier\": \"1.2.0\", \"category\": \"lifecycle\"}'\n\n# Track app uninstall\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"uninstall\", \"category\": \"lifecycle\"}'\n```\n\n### User Engagement\n\nMeasure how users interact with your app without tracking personal data:\n\n```bash\n# Track page views\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"page_view\", \"qualifier\": \"home\"}'\n\n# Track feature usage\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"feature_used\", \"qualifier\": \"dark_mode\"}'\n\n# Track button clicks\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"button_click\", \"qualifier\": \"signup\"}'\n```\n\n### Revenue Events\n\nMonitor purchase and subscription events:\n\n```bash\n# Track one-time purchase\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"purchase\", \"qualifier\": \"premium_upgrade\", \"value\": 999}'\n\n# Track subscription start\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"subscription\", \"qualifier\": \"monthly_plan\", \"value\": 499}'\n\n# Track in-app purchase\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"iap\", \"qualifier\": \"coins_pack\", \"value\": 299}'\n```\n\n## Setup\n\n### Prerequisites\n\n- [Node.js](https://nodejs.org/) (version 14 or higher)\n- [Cloudflare account](https://dash.cloudflare.com/sign-up)\n- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/get-started/)\n\n### Installation\n\n1. Clone this repository\n```bash\ngit clone https://github.com/arraypress/cloudflare-applytics.git\ncd cloudflare-applytics\n```\n\n2. Install dependencies\n```bash\nnpm install\n```\n\n3. Update your `wrangler.toml` configuration\n```toml\nname = \"cloudflare-applytics\"\nmain = \"src/index.js\"\ncompatibility_date = \"2023-10-02\"\n\n[vars]\nENVIRONMENT = \"development\"\nAPI_KEY = \"your-development-api-key\"\n\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"applytics_local\"\ndatabase_id = \"applytics-local\"\n```\n\n### Database Setup\n\n1. Create the D1 database (only needed once)\n```bash\nwrangler d1 create applytics_local\n# Update the database_id in wrangler.toml with the ID returned\n```\n\n2. Initialize the database with schema\n```bash\nnpm run setup\n```\n\n3. Check that tables were created successfully\n```bash\nnpm run db\n```\n\n### Development\n\nStart the local development server:\n```bash\nnpm run dev\n```\n\n### Available Scripts\n\n```bash\n# Development\nnpm run dev         # Start local development server\nnpm run build       # Build the project\n\n# Deployment\nnpm run deploy      # Deploy to development environment\nnpm run deploy:prod # Deploy to production environment\n\n# Database Setup \u0026 Management\nnpm run setup       # Setup local database with schema\nnpm run setup:prod  # Setup production database with schema\nnpm run db          # Show database tables\nnpm run db:info     # Show database table information\nnpm run db:events   # Show all events\nnpm run db:stats    # Show all stats\nnpm run db:reset    # Reset database (deletes all data)\n```\n\n### Production Deployment\n\n1. Update production configuration in `wrangler.toml`\n\n2. Set API key as a secret:\n```bash\nwrangler secret put API_KEY --env production\n```\n\n3. Deploy to production:\n```bash\nnpm run deploy:prod\n```\n\n## Understanding Events and Stats\n\n### Events vs. Stats\n\n**Events** are individual occurrences tracked in real-time:\n- Each event is recorded with a timestamp\n- Events have types, optional qualifiers, and values\n- Events are stored in the events table\n- Used for historical analysis and timeseries data\n\n**Stats** are cumulative counters derived from events:\n- Automatically updated when events are tracked\n- Represent aggregate metrics (e.g., total page views)\n- Stored in the stats table for efficient querying\n- Used for dashboards and overall analytics\n\n### Event Anatomy\n\nEvents consist of:\n- `event_type`: The main action being tracked (e.g., \"page_view\", \"purchase\")\n- `qualifier`: Optional sub-type or specific instance (e.g., \"home\", \"premium_plan\")\n- `value`: Numeric value associated with the event (default: 1)\n- `category`: Organizational group (e.g., \"engagement\", \"revenue\")\n- `country`: Country code (automatically detected or can be provided)\n- `timestamp`: When the event occurred (defaults to current time)\n\nWhen an event is tracked, it creates or updates a corresponding stat with the key format `event_type.qualifier`.\n\n### Practical Examples\n\n**Example 1: Tracking App Usage**\n\nTrack daily active users by recording a \"session\" event when your app starts:\n\n```bash\n# User opens the app - track a session start\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"session\", \"qualifier\": \"start\"}'\n\n# Later, analyze daily active users\ncurl \"https://your-worker.workers.dev/timeseries?metric=session.start\u0026period=day\u0026limit=30\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\"\n```\n\n**Example 2: Feature Adoption**\n\nTrack how many users enable a new feature:\n\n```bash\n# User enables dark mode\ncurl -X POST \"https://your-worker.workers.dev/track\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"event_type\": \"feature_enabled\", \"qualifier\": \"dark_mode\"}'\n\n# Get total count of feature enablement\ncurl \"https://your-worker.workers.dev/stats?prefix=feature_enabled\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\"\n```\n\n**Example 3: Conversion Funnel**\n\nTrack user progression through a signup flow:\n\n```bash\n# Track each step in the signup flow\ncurl -X POST \"https://your-worker.workers.dev/track/batch\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"events\": [\n      {\"event_type\": \"signup_step\", \"qualifier\": \"view_form\"},\n      {\"event_type\": \"signup_step\", \"qualifier\": \"submit_email\"},\n      {\"event_type\": \"signup_step\", \"qualifier\": \"verify_email\"},\n      {\"event_type\": \"signup_step\", \"qualifier\": \"complete\"}\n    ]\n  }'\n\n# Compare conversion rates between steps\ncurl \"https://your-worker.workers.dev/timeseries?metrics=signup_step.view_form,signup_step.submit_email,signup_step.verify_email,signup_step.complete\u0026period=day\" \\\n  -H \"X-App-ID: your-app-id\" \\\n  -H \"X-API-Key: your-api-key\"\n```\n\n## API Documentation\n\n### Authentication\n\nAll API requests require the following headers:\n- `X-App-ID`: Your application ID\n- `X-API-Key`: Your API key\n\n### Event Tracking\n\n#### POST /track\nTrack a single event.\n\n**Request Body:**\n```json\n{\n  \"event_type\": \"page_view\",\n  \"qualifier\": \"home\",\n  \"value\": 1,\n  \"category\": \"engagement\",\n  \"timestamp\": 1678912345,\n  \"country\": \"US\"\n}\n```\n\n**Parameters:**\n- `event_type` (required): Type of event\n- `qualifier` (optional): Event qualifier\n- `value` (optional): Numeric value, defaults to 1\n- `category` (optional): Event category\n- `timestamp` (optional): Unix timestamp, defaults to current time\n- `country` (optional): Country code, automatically detected from request if not provided\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"app_id\": \"app1\",\n  \"metric\": \"page_view.home\",\n  \"category\": \"engagement\",\n  \"value\": 42,\n  \"timestamp\": 1678912345,\n  \"country\": \"US\"\n}\n```\n\n#### POST /track/batch\nTrack multiple events in a single request.\n\n**Request Body:**\n```json\n{\n  \"events\": [\n    {\n      \"event_type\": \"page_view\",\n      \"qualifier\": \"home\",\n      \"value\": 1\n    },\n    {\n      \"event_type\": \"button_click\",\n      \"qualifier\": \"signup\",\n      \"category\": \"interaction\"\n    }\n  ]\n}\n```\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"app_id\": \"app1\",\n  \"processed\": 2,\n  \"events\": [\n    {\n      \"event_type\": \"page_view\",\n      \"qualifier\": \"home\",\n      \"metric\": \"page_view.home\",\n      \"category\": \"engagement\",\n      \"country\": \"US\"\n    },\n    {\n      \"event_type\": \"button_click\",\n      \"qualifier\": \"signup\",\n      \"metric\": \"button_click.signup\",\n      \"category\": \"interaction\",\n      \"country\": \"US\"\n    }\n  ]\n}\n```\n\n### Stats Endpoints\n\n#### GET /stats\nGet all stats for an application.\n\n**Query Parameters:**\n- `prefix` (optional): Filter metrics starting with prefix\n- `category` (optional): Filter metrics by category\n- `format` (optional): Response format, either 'simple' (default) or 'detailed'\n- `view` (optional): View type, supports 'default', 'category', 'top'\n- `paginate` (optional): Enable pagination (true/false)\n- `page` (optional): Page number for pagination\n- `pageSize` (optional): Items per page for pagination\n\n**Response (simple format):**\n```json\n{\n  \"page_view.home\": 42,\n  \"button_click.signup\": 18\n}\n```\n\n**Response (detailed format):**\n```json\n{\n  \"app_id\": \"app1\",\n  \"filters\": {\n    \"prefix\": null,\n    \"category\": \"engagement\",\n    \"country\": null\n  },\n  \"data\": [\n    {\n      \"metric\": \"page_view.home\",\n      \"value\": 42,\n      \"category\": \"engagement\",\n      \"last_updated\": \"2025-03-09T12:34:56Z\"\n    }\n  ]\n}\n```\n\n#### GET /stats?view=top\nGet top metrics by value.\n\n**Query Parameters:**\n- `category` (optional): Filter by category\n- `limit` (optional): Maximum number of results, defaults to 10\n- `sort` (optional): Sort direction, 'asc' or 'desc' (default)\n\n**Response:**\n```json\n{\n  \"app_id\": \"app1\",\n  \"category\": \"all\",\n  \"sort\": \"desc\",\n  \"metrics\": [\n    {\n      \"metric\": \"page_view.home\",\n      \"value\": 42,\n      \"category\": \"engagement\",\n      \"last_updated\": \"2025-03-09T12:34:56Z\"\n    }\n  ]\n}\n```\n\n#### GET /stats?view=category\nGet stats grouped by category.\n\n**Response:**\n```json\n{\n  \"app_id\": \"app1\",\n  \"groupBy\": \"category\",\n  \"data\": {\n    \"engagement\": 156,\n    \"revenue\": 2850,\n    \"lifecycle\": 42\n  }\n}\n```\n\n### Timeseries Endpoints\n\n#### GET /timeseries\nGet time-based data for a specific metric or multiple metrics.\n\n**Query Parameters:**\n- `metric` (required if metrics not provided): Single metric name\n- `metrics` (required if metric not provided): Comma-separated list of metrics to compare\n- `period` (optional): Time grouping, one of 'hour', 'day', 'week', 'month', defaults to 'day'\n- `limit` (optional): Maximum number of data points, defaults to 30\n- `from` (optional): Start date (Unix timestamp or ISO format)\n- `to` (optional): End date (Unix timestamp or ISO format)\n- `country` (optional): Filter by country code\n\n**Response for single metric:**\n```json\n{\n  \"app_id\": \"app1\",\n  \"metric\": \"page_view.home\",\n  \"period\": \"day\",\n  \"from\": \"2025-02-07T00:00:00Z\",\n  \"to\": \"2025-03-09T00:00:00Z\",\n  \"country\": null,\n  \"data\": [\n    {\n      \"time_period\": \"2025-03-08\",\n      \"total\": 15\n    },\n    {\n      \"time_period\": \"2025-03-09\",\n      \"total\": 27\n    }\n  ]\n}\n```\n\n**Response for multiple metrics:**\n```json\n{\n  \"app_id\": \"app1\",\n  \"metrics\": [\"page_view.home\", \"page_view.settings\"],\n  \"period\": \"day\",\n  \"from\": \"2025-02-07T00:00:00Z\",\n  \"to\": \"2025-03-09T00:00:00Z\",\n  \"country\": null,\n  \"data\": [\n    {\n      \"time_period\": \"2025-03-08\",\n      \"page_view.home\": 15,\n      \"page_view.settings\": 8\n    },\n    {\n      \"time_period\": \"2025-03-09\",\n      \"page_view.home\": 27,\n      \"page_view.settings\": 12\n    }\n  ]\n}\n```\n\n### Events Endpoints\n\n#### GET /events\nGet event history for an application.\n\n**Query Parameters:**\n- `type` (optional): Filter by event type\n- `category` (optional): Filter by event category\n- `country` (optional): Filter by country code\n- `limit` (optional): Maximum number of events to return, defaults to 50\n- `from` (optional): Start date (Unix timestamp or ISO format), defaults to 24 hours ago\n\n**Response:**\n```json\n{\n  \"app_id\": \"app1\",\n  \"from\": \"2025-03-08T12:34:56Z\",\n  \"count\": 2,\n  \"events\": [\n    {\n      \"id\": 123,\n      \"event_type\": \"page_view\",\n      \"event_category\": \"engagement\",\n      \"qualifier\": \"home\",\n      \"value\": 1,\n      \"country\": \"US\",\n      \"timestamp\": \"2025-03-09T12:34:56Z\"\n    },\n    {\n      \"id\": 124,\n      \"event_type\": \"button_click\",\n      \"event_category\": \"interaction\",\n      \"qualifier\": \"signup\",\n      \"value\": 1,\n      \"country\": \"US\",\n      \"timestamp\": \"2025-03-09T12:35:23Z\"\n    }\n  ]\n}\n```\n\n### App Endpoints\n\n#### GET /app\nGet information about a specific app.\n\n**Query Parameters:**\n- `view` (optional): View type, either 'summary' (default) or 'dashboard'\n\n**Response (summary view):**\n```json\n{\n  \"app_id\": \"app1\",\n  \"total_events\": 1542,\n  \"first_event\": \"2025-01-15T08:23:45Z\",\n  \"last_event\": \"2025-03-09T12:34:56Z\",\n  \"metrics_count\": 25,\n  \"recent_events\": [\n    {\n      \"event_type\": \"page_view\",\n      \"qualifier\": \"home\",\n      \"category\": \"engagement\",\n      \"country\": \"US\",\n      \"timestamp\": \"2025-03-09T12:34:56Z\"\n    }\n  ]\n}\n```\n\n#### GET /app?view=dashboard\nGet dashboard data for an application.\n\n**Response:**\n```json\n{\n  \"app_id\": \"app1\",\n  \"summary\": {\n    \"total_events\": 1542,\n    \"first_event\": \"2025-01-15T08:23:45Z\",\n    \"last_event\": \"2025-03-09T12:34:56Z\",\n    \"metrics_count\": 25\n  },\n  \"recent_activity\": {\n    \"last_24h_events\": 145,\n    \"last_24h_value\": 198\n  },\n  \"categories\": [\n    {\n      \"category\": \"engagement\",\n      \"total\": 856\n    },\n    {\n      \"category\": \"revenue\",\n      \"total\": 3540\n    }\n  ],\n  \"top_countries\": [\n    {\n      \"country\": \"US\",\n      \"event_count\": 523,\n      \"value_sum\": 1245\n    },\n    {\n      \"country\": \"GB\",\n      \"event_count\": 218,\n      \"value_sum\": 532\n    }\n  ]\n}\n```\n\n#### GET /apps\nGet a list of all available apps.\n\n**Query Parameters:**\n- `stats` (optional): Include stats summary for each app (true/false)\n\n**Response:**\n```json\n{\n  \"apps\": [\"app1\", \"app2\", \"app3\"]\n}\n```\n\n**Response with stats:**\n```json\n{\n  \"apps\": [\n    {\n      \"app_id\": \"app1\",\n      \"total_events\": 1542,\n      \"first_event\": \"2025-01-15T08:23:45Z\",\n      \"last_event\": \"2025-03-09T12:34:56Z\",\n      \"metrics_count\": 25\n    },\n    {\n      \"app_id\": \"app2\",\n      \"total_events\": 856,\n      \"first_event\": \"2025-02-03T10:15:32Z\",\n      \"last_event\": \"2025-03-09T11:22:33Z\",\n      \"metrics_count\": 18\n    }\n  ]\n}\n```\n\n## Client Libraries\n\n- [ApplyticsKit](https://github.com/arraypress/ApplyticsKit) - Swift client for iOS/macOS\n- More coming soon!\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farraypress%2Fapplytics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farraypress%2Fapplytics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farraypress%2Fapplytics/lists"}