Skip to content

chore: remove the Postgres API and syncer - #700

Open
nelitow wants to merge 21 commits into
mainfrom
nj/chore/drop-postgres-backend
Open

chore: remove the Postgres API and syncer#700
nelitow wants to merge 21 commits into
mainfrom
nj/chore/drop-postgres-backend

Conversation

@nelitow

@nelitow nelitow commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked on #699. The explorer is now served by packages/api-lite, so the Postgres-backed GraphQL API, the syncer, the cosmos worker and everything that only existed to build, deploy or run them are removed. The @fuel-explorer/graphql package stays as a library: schemas, generated SDK types, the domain layer and value objects that both the frontend and api-lite import. 202 files changed, 41,523 lines deleted.

Changes

Area Change
graphql package Deleted the Express and Yoga server, syncer, cosmos worker, pm2 configs, Postgres DAOs, repositories, migrations, workers and their tests; kept schemas, SDK, domain and value objects; runtime dependencies 36 to 9, dev dependencies 31 to 21
graphql config config.ts reads only FUEL_PROVIDER, FUEL_CHAIN and DEBUG; .env.example trimmed to those, .env.production deleted
Deployment Deleted the helm chart, cluster and minikube scripts, Tiltfile, deployment/Dockerfile, the docker-publish and helm-publish workflows, and the run and setup backend scripts
Local docker Dropped the Postgres service, the indexer env file and the DB variables from docker/docker-compose.yml and docker/envs; the bridge profile used by e2e is unchanged
Root scripts Removed run:backend, setup:backend, dev:graphql and dev:graphql:all
Docs docs/GETTING_STARTED.md and the graphql README describe the api-lite setup

nelitow and others added 7 commits August 28, 2026 16:49
… formatting

- rate-limit key from X-Forwarded-For behind the Railway edge
- Cosmos poller keeps its cursor on non-2xx responses
- staking event pagination orders on (block_height, _id)
- withdraw proof cache no longer stores failed responses
- DataCache bounded to 1000 entries with LRU eviction
- hot-key decay timer catches sqlite errors
- per-minute TPS buckets, GET /api/charts with Cache-Control and nginx proxy_cache, home charts fetch it first
- fee chart USD values use convertToUsd formatting and full-hour buckets
- biome format on loadtest scripts
@nelitow
nelitow requested a review from helciofranco as a code owner August 29, 2026 11:09
@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
fuel-explorer-v2-vite Ready Ready Preview Sep 3, 2026 5:48pm UTC

Request Review

@cursor

cursor Bot commented Aug 29, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Removes established Helm/GHCR image publish and Postgres-indexer local/docker paths while shifting production-style deployment to api-lite + nginx; misconfiguration or missing migration breaks deploys and monitoring expectations.

Overview
Retires the Postgres-backed indexer deployment path (Helm/cluster charts, deployment/Dockerfile, Tilt/minikube scripts, docker-publish / helm-publish workflows) and strips indexer Postgres and related env from local docker/docker-compose.yml.

Adds a docker/vps/ stack to run api-lite plus an nginx-served explorer: multi-stage Dockerfiles, compose profiles for a 1 GB VPS sim and GHCR-based prod, nginx proxy/rate-limit//api/charts edge cache, Railway JSON config, and a deploy runbook (Railway + DigitalOcean).

CI gains a scheduled api-lite-uptime probe (/api/health, lag, home, charts) and a tests-e2e-api-lite Playwright job in PR tests. Docs point new contributors at api-lite instead of the old API/syncer; docker/vps/.env is excluded from Docker/Railway builds.

Reviewed by Cursor Bugbot for commit e3be466. Bugbot is set up for automated code reviews on this repo. Configure here.

- empty environment variables are treated as unset by loadConfig
- nginx listens on IPv6 as well
- rate limit keys on the TCP peer by default; RATE_LIMIT_KEY=$xff_last opts into the last X-Forwarded-For hop behind a proxy
- assets, contracts and predicates are exempt from index retention; asset lookups fall back to fuel-core assetDetails and the verified-assets registry; registry assets are seeded on boot
- suspicious flag derived from subId collisions with the registry
- USD values for base-asset fees, inputs, outputs, mints and balances from the ETH price
- exact 1-based counts on account, contract and block transaction lists and on minted assets
- transactionsByOwner no longer falls back to fuel-core on forward paging past the index; fallback block rendering is parallel and bounded
- search returns null and logs on malformed input or lookup failures
- dashboard and chart series serve numbers, timestamps in milliseconds, per-bucket max tps and gas
- utxos cap 2500 like production
- scheduled uptime probe workflow for the explorer
@nelitow
nelitow force-pushed the nj/chore/drop-postgres-backend branch from 6fc4493 to 520232d Compare August 29, 2026 19:07
- Playwright suite under tests/api-lite: home, block, transaction, account, search and staking pages render real data from api-lite in RPC mode against the local node
- playwright-api-lite.config.ts boots api-lite on 3004 and the vite preview on 3000
- tests-e2e-api-lite job in pr-tests.yml
@nelitow
nelitow force-pushed the nj/chore/drop-postgres-backend branch from 6f198d6 to 42f74cc Compare August 29, 2026 19:19
- fuel-core fallback pages number from the oldest end instead of reusing the newest ranks
- a failed fallback render is backfilled from the remaining items instead of shrinking the page
- list counts capped at 1001 and bounded to the contiguous index window
- asset registry matches compare lowercase ids
- REST errors distinguish client from upstream: ValidationError maps to 400, a missing event to 404, everything else to 502 with a server-side log
- index writes happen before the disk-cache write, so a crash between them re-fetches instead of leaving a pinned block unindexed
- backfill clamps indexed_from to the retention floor when retention advanced it mid-fetch (real race found by the new regression test)
- backfill gaps surfaced in /health
- a 200 cosmos response without a tx_responses array no longer advances the cursor
- long-lived viem client, single index.db path constant, decoder policies trimmed to the schema-backed four
- block cache accounting is heap-aware: a measured 2.25x object-vs-JSON multiplier (applied as 2.5x) counts against the budget
- 768 MB heap in the image with the memory math documented; the 960 MB compose tier overrides the cache to 48 MB
- backfill batch size wired to config instead of a silent 20
- eviction under pinning terminates and logs once per minute
- Railway restart policy retries 100 times instead of stranding the service after a crash loop
@nelitow
nelitow force-pushed the nj/chore/drop-postgres-backend branch from 9240d8d to f4e5136 Compare August 31, 2026 12:49
@cursor

cursor Bot commented Aug 31, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

Base automatically changed from nj/feat/el-cheapo-explorer to main September 3, 2026 17:05
nelitow and others added 2 commits September 3, 2026 14:47
…d hop is a Cloudflare address

The explorer's public domain is a Cloudflare-proxied CNAME to Railway, so the first X-Forwarded-For hop is a Cloudflare address for every visitor. A geo block over Cloudflare's published ranges, fetched at image build, selects CF-Connecting-IP only for those requests; direct traffic keeps the first hop, which Railway's edge rewrites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…i-lite

- delete the graphql package's server, syncer, cosmos worker, pm2 configs, Postgres DAOs, migrations and tests; keep schemas, generated SDK, domain layer and value objects as a library
- delete the helm chart, cluster/minikube scripts, Tiltfile, deployment/Dockerfile, the docker-publish and helm-publish workflows and the run/setup backend scripts
- drop the Postgres service and indexer env from docker/docker-compose.yml
- graphql package dependencies 36 to 9, devDependencies 31 to 21
@nelitow
nelitow force-pushed the nj/chore/drop-postgres-backend branch from f4e5136 to e3be466 Compare September 3, 2026 17:47
Comment on lines +10 to +28
runs-on: ubuntu-latest
timeout-minutes: 5
env:
EXPLORER_URL: ${{ vars.API_LITE_EXPLORER_URL || 'https://explorer-production-f355.up.railway.app' }}
MAX_LAG_BLOCKS: "120"
steps:
- name: Health and index lag
run: |
body=$(curl -fsS --max-time 20 "$EXPLORER_URL/api/health")
echo "$body"
ok=$(echo "$body" | jq -r '.ok')
lag=$(echo "$body" | jq -r '.lag')
test "$ok" = "true"
test "$lag" -le "$MAX_LAG_BLOCKS"
- name: Home page
run: curl -fsS --max-time 20 -o /dev/null "$EXPLORER_URL/"
- name: Charts endpoint
run: |
curl -fsS --max-time 20 "$EXPLORER_URL/api/charts" | jq -e '.statistics and .tps' > /dev/null
Comment on lines +211 to +275
name: E2E Tests api-lite
runs-on: warp-ubuntu-latest-x64-4x
timeout-minutes: 14
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TURBO_REMOTE_ONLY: true
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Cache turbo build setup
uses: actions/cache@v4
with:
path: .turbo
# Distinguish between a normal build and a E2E build, which disables code splitting for faster builds
key: ${{ runner.os }}-e2e-turbo-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-e2e-turbo-

- name: Setup Node
uses: FuelLabs/github-actions/setups/node@master
with:
node-version: 20.15.1
pnpm-version: 9.10.0

- name: Create .env file explorer
run: cp packages/app-explorer/.env.example packages/app-explorer/.env

- name: Build production version of explorer
env:
NODE_ENV: test
run: pnpm build:prod

- name: Setup Docker
uses: FuelLabs/github-actions/setups/docker@master
with:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Start Test Node
timeout-minutes: 7
run: pnpm node:start

- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps chromium

- name: Run E2E Tests
run: xvfb-run --auto-servernum -- pnpm test:e2e-api-lite
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: playwright-report/api-lite/results.json
PLAYWRIGHT_FORCE_TTY: true
PLAYWRIGHT_FORCE_COLOR: true

- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-api-lite
path: packages/e2e-tests/playwright-report/api-lite/
retention-days: 30

- name: Stop Test Node
run: pnpm node:stop

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e3be466. Configure here.

{ sed 's/$/ 1;/' /tmp/ips-v4; sed 's/$/ 1;/' /tmp/ips-v6; } > /etc/nginx/cloudflare-ips.conf; \
rm -f /tmp/ips-v4 /tmp/ips-v6
COPY docker/vps/nginx.conf.template /etc/nginx/templates/default.conf.template
ENV API_UPSTREAM=api:3000 NGINX_RESOLVER=127.0.0.11 RATE_LIMIT_KEY='$binary_remote_addr' NGINX_ENVSUBST_FILTER='^(API_UPSTREAM|NGINX_RESOLVER|RATE_LIMIT_KEY)$'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nginx rate-limit key is empty

High Severity

RATE_LIMIT_KEY is assigned '$binary_remote_addr' in a Dockerfile ENV. Docker expands $ variables even inside single quotes, so the value becomes empty at image build. Local compose and the droplet compose never override it, and nginx envsubst then writes an invalid limit_req_zone directive, so the explorer container exits on startup.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e3be466. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants