Skip to content

feat(api): add app listing endpoint#6491

Merged
ogzhanolguncu merged 8 commits into
mainfrom
06-18-feat_api_add_api_listing_endpoint
Jul 1, 2026
Merged

feat(api): add app listing endpoint#6491
ogzhanolguncu merged 8 commits into
mainfrom
06-18-feat_api_add_api_listing_endpoint

Conversation

@ogzhanolguncu

@ogzhanolguncu ogzhanolguncu commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Important

Unkey is not accepting external pull requests at this time. Pull requests from people outside the Unkey team will not be reviewed or merged.

What does this PR do?

Adds the POST /v2/apps.listApps endpoint, which returns a cursor-paginated list of apps belonging to a given project.

The SQL query backing this endpoint was updated to support cursor-based pagination: it now filters by id >= id_cursor, orders by id ASC, and accepts a LIMIT parameter. Previously the query ordered by created_at with no pagination support.

The handler:

  • Looks up the project by workspace and ID, returning 404 if not found
  • Enforces project.*.read_app or project.<project_id>.read_app permissions, returning a 404 (rather than 403) on authorization failure to avoid leaking project existence to unauthorized callers
  • Fetches limit + 1 rows to determine whether more pages exist, then trims the result and sets the cursor and hasMore fields in the pagination response

OpenAPI schemas and the generated YAML were updated to include V2AppsListAppsRequestBody (with projectId, limit, and cursor fields) and V2AppsListAppsResponseBody (with data, meta, and pagination fields). Speakeasy cursor pagination metadata is included.

Tests cover:

  • 200: empty project, populated project, cross-project isolation, non-existent cursor, cursor borrowed from a foreign workspace, and full pagination traversal
  • 400: missing/invalid projectId, out-of-range limit
  • 401: invalid bearer token
  • 403/404: missing permissions, cross-workspace project access, and an existence oracle test asserting that unauthorized responses for real and nonexistent projects are indistinguishable

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Call POST /v2/apps.listApps with a valid root key and a projectId that belongs to your workspace; verify a 200 response with the correct apps and pagination fields
  • Call with limit: 2 against a project with more than 2 apps and follow the cursor across pages; verify all apps are returned exactly once and the final page has hasMore: false and no cursor
  • Call with a root key that lacks read_app permission; verify a 404 is returned and no project or app IDs are present in the response body
  • Call with an invalid projectId (too short, invalid characters, too long) or an out-of-range limit; verify a 400 validation error is returned
  • Run the unit tests in svc/api/routes/v2_apps_list_apps/

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Internal Workflow Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Ran mise run fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
dashboard Ready Ready Preview, Comment Jul 1, 2026 1:11pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
design Ignored Ignored Preview Jul 1, 2026 1:11pm

Request Review

@ogzhanolguncu ogzhanolguncu mentioned this pull request Jun 18, 2026
19 tasks
@ogzhanolguncu ogzhanolguncu force-pushed the 06-17-feat_api_add_get_app branch from b11231d to e2f1e95 Compare June 19, 2026 09:58
@ogzhanolguncu ogzhanolguncu force-pushed the 06-18-feat_api_add_api_listing_endpoint branch from 2c3a04c to 07f034c Compare June 19, 2026 09:58
@ogzhanolguncu ogzhanolguncu force-pushed the 06-17-feat_api_add_get_app branch from e2f1e95 to 3e476a4 Compare June 19, 2026 13:39
@ogzhanolguncu ogzhanolguncu force-pushed the 06-18-feat_api_add_api_listing_endpoint branch from 07f034c to 2d983d3 Compare June 19, 2026 13:39
@ogzhanolguncu ogzhanolguncu force-pushed the 06-17-feat_api_add_get_app branch from 3e476a4 to d11c4c6 Compare June 19, 2026 14:10
@ogzhanolguncu ogzhanolguncu force-pushed the 06-18-feat_api_add_api_listing_endpoint branch from 2d983d3 to 41476b1 Compare June 19, 2026 14:10
@ogzhanolguncu ogzhanolguncu force-pushed the 06-17-feat_api_add_get_app branch from d11c4c6 to a67c015 Compare June 19, 2026 14:20
@ogzhanolguncu ogzhanolguncu force-pushed the 06-18-feat_api_add_api_listing_endpoint branch from 41476b1 to 2cb7c32 Compare June 19, 2026 14:20
@ogzhanolguncu ogzhanolguncu force-pushed the 06-18-feat_api_add_api_listing_endpoint branch from 2cb7c32 to dbc46ae Compare June 19, 2026 15:27
Comment thread pkg/db/queries/app_list_by_project.sql Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — since the prior pullfrog review (720e626), the ListAppsByProject query stopped embedding the full apps row; this run re-reviewed that delta (commit 9360c70).

  • sqlc.embed(apps) replaced with SELECT apps.*pkg/db/queries/app_list_by_project.sql no longer embeds, so the regenerated ListAppsByProject returns []App instead of []ListAppsByProjectRow (the .App-wrapper struct is gone from app_list_by_project.sql_generated.go and querier_generated.go).
  • Handler updated in lockstepsvc/api/routes/v2_apps_list_apps/handler.go reads row.ID / row.Name / row.ProjectID etc. directly off the flattened App row; the cursor-pagination, RBAC, and 404-on-authz-failure logic are untouched.

The flattening is mechanically correct and self-consistent: the Scan targets and the handler field accesses both move off the .App wrapper together, and the response mapping is unchanged. This resolves Flo4604's inline question on the embed. The cursor logic flagged informationally in the initial review remains untouched.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Claude Opus𝕏

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