Skip to content

Implements a public REST API for SplitPro using @trpc/openapi, with OpenAPI 3.1 spec generation, interactive documentation (Scalar), and offset-based pagination#707

Open
kushpvo wants to merge 15 commits into
oss-apps:mainfrom
kushpvo:feat/trcp-openapi

Conversation

@kushpvo

@kushpvo kushpvo commented Jul 17, 2026

Copy link
Copy Markdown

Description

Closes #615 — implements a public REST API for SplitPro using @trpc/openapi, with
OpenAPI 3.1 spec generation, interactive documentation (Scalar), and offset-based
pagination.

API surface (/api/v1/*)

All endpoints authenticate via API key: Authorization: Bearer spro_<key>.

Endpoint Method Description
user.me GET Authenticated user profile
user.getFriends GET List of friends with balances
user.getOwnExpenses GET Expenses paid by the user (paginated)
user.getBalancesWithFriend GET Per-group balances with a specific friend
user.getFriend GET Friend profile + default split config
group.getAllGroups GET All groups the user belongs to
group.getGroupDetails GET Single group with members, balances, default split
expense.getBalances GET Per-friend balances across all groups
expense.getAllExpenses GET All expenses the user participates in (paginated)
expense.getExpensesWithFriend GET Expense history with a specific friend (paginated)
expense.getGroupExpenses GET All expenses in a group (paginated)
expense.getExpenseDetails GET Full expense detail (participants, notes, recurrence)
expense.addOrEditExpense POST Create or update expenses (array body)

Input formats

All GET endpoints accept three equivalent input formats:

  • Individual query params: ?groupId=150&limit=20
  • Plain JSON: ?input={"groupId":150}
  • Native superjson: ?input={"json":{"groupId":150}}

POST bodies are accepted as plain JSON (the API transformer handles superjson
transparently for both directions).

Pagination

Four list endpoints use offset-based pagination:

  • Input: ?limit=20&offset=0 (defaults: limit=20, offset=0, max limit=100)
  • Response: { items: [...], pagination: { total, limit, offset, hasMore } }
  • Defaults are emitted in the OpenAPI spec and shown in the Scalar UI
  • Unpaginated procedure variants continue to serve the cookie-auth app (no UI breakage)

OpenAPI docs

  • Interactive docs (Scalar): /api/docs
  • Raw spec: /api/openapi.json
  • Generated at build time via pnpm gen:openapi
  • Request body schemas inferred from Zod input schemas
  • Bearer auth declared globally

Architecture

  • src/server/api/apiRouter.ts — curated public API router (separate from appRouter)
  • src/pages/api/v1/[trpc].ts — handler with API-key context + query param bridge
  • src/server/api/pagination.ts — shared offset-pagination helpers
  • scripts/generate-openapi.ts — build-time spec generation + post-processing (injects defaults)
  • Procedures are exported standalone from router files and imported by both appRouter and apiRouter

Demo

Checklist

  • I have read CONTRIBUTING.md in its entirety
  • I have performed a self-review of my own code
  • I have added unit tests to cover my changes
  • The last commit successfully passed pre-commit checks
  • Any AI code was thoroughly reviewed by me

kvora32 and others added 14 commits July 15, 2026 13:35
Adds an isolated public REST surface generated by @trpc/openapi (alpha):
- apiRouter (curated subset) served at /api/v1 with API-key auth via createApiContext
- ApiKey model (sha256-hashed, spro_ prefix) + create/list/revoke procedures + Account UI
- OpenAPI spec at /api/openapi.json and Scalar docs at /api/docs
- first exposed endpoint: user.me

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add shared pagination module (paginationInput schema + paginatedResult helper)
- Create API-specific paginated variants for getAllExpenses, getExpensesWithFriend,
  getGroupExpenses, and getOwnExpenses (app procedures unchanged)
- All four endpoints return { items, pagination: { total, limit, offset, hasMore } }
- Defaults: limit=20, offset=0, max limit=100
- Post-process generated OpenAPI spec to inject default values for limit/offset
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.

Minimal API

1 participant