Skip to content

feat: implement GET /api/bounties/:id — bounty detail endpoint#41

Open
kenchambers wants to merge 1 commit intodevasignhq:mainfrom
kenchambers:feat/bounty-detail-endpoint
Open

feat: implement GET /api/bounties/:id — bounty detail endpoint#41
kenchambers wants to merge 1 commit intodevasignhq:mainfrom
kenchambers:feat/bounty-detail-endpoint

Conversation

@kenchambers
Copy link

Summary

Closes #21

Implements the GET /api/bounties/:id endpoint to return full bounty details including creator info, application count, assignee info, and current status.


Changes

Backend — packages/api/src/index.ts

  • Added GET /api/bounties/:id route to the Hono server
  • Proxies to the main DevAsign API (configurable via DEVASIGN_API_URL env var, defaults to https://api.devasign.com)
  • Forwards Authorization header for authenticated requests
  • Returns 404 when the bounty is not found
  • Returns 502 on upstream API failure with a descriptive error message
  • Validates input: rejects blank/missing IDs with 400

Types — packages/mobile/types.ts

  • Added applicationCount?: number to Bounty interface
  • Added assignee?: { username: string; avatarUrl: string } | null to represent the assigned developer

Frontend — packages/mobile/pages/BountyDetail.tsx

  • Replaced MOCK_BOUNTIES.find() with a real fetch() call to /api/bounties/:id
  • Added loading state (animated placeholder) and error state
  • AbortController cleanup on component unmount
  • Displays applicationCount with proper pluralisation (3 applications)
  • Displays an assignee card (avatar + username) when the bounty is assigned
  • Displays an "Unassigned / Open for applications" card when status is Open and no assignee exists
  • The Apply for Bounty CTA is now only shown for Open, unassigned bounties

Environment Variable

Variable Description Default
DEVASIGN_API_URL Base URL for upstream DevAsign API https://api.devasign.com

Testing

# Start the API
cd packages/api && npm run dev

# In another terminal, test the new endpoint
curl http://localhost:3001/api/bounties/<id>          # 200 OK
curl http://localhost:3001/api/bounties/nonexistent   # 404
curl http://localhost:3001/api/bounties/              # 404 (missing id)

Closes devasignhq#21

## Summary

Implements the GET /api/bounties/:id endpoint to return full bounty
details including creator info, application count, assignee info,
and current status.

### Backend (packages/api/src/index.ts)
- Added GET /api/bounties/:id route to the Hono server
- Proxies to the main DevAsign API (DEVASIGN_API_URL env var)
- Forwards Authorization header for authenticated requests
- Returns structured 404 when bounty not found
- Returns 502 on upstream API failure with descriptive error
- Input validated: rejects empty/missing IDs

### Types (packages/mobile/types.ts)
- Added optional applicationCount?: number to Bounty interface
- Added optional assignee?: { username, avatarUrl } | null to Bounty
  to represent the assigned developer if one exists

### Frontend (packages/mobile/pages/BountyDetail.tsx)
- Replaced MOCK_BOUNTIES.find() with real fetch() to /api/bounties/:id
- Added loading and error states for async data fetching
- AbortController cleanup on component unmount
- Shows applicationCount with pluralisation (e.g. '3 applications')
- Shows assignee card with avatar and username when bounty is assigned
- Shows 'Unassigned / Open for applications' when status is Open and
  no assignee exists
- 'Apply for Bounty' CTA now only renders for Open, unassigned bounties

### Environment
- DEVASIGN_API_URL: upstream API base URL (default: https://api.devasign.com)
@devasign-app
Copy link

devasign-app bot commented Feb 18, 2026

🟡 AI Code Review Results

Status: Review Recommended
Confidence: 95%


🟡 Merge Score: 75/100

🟡 ███████████████░░░░░ 75%

Recommendation: ⚠️ This PR is mostly good but could benefit from some improvements before merging.

This is a solid feature implementation that successfully creates a new BFF endpoint and a corresponding frontend detail page. The code is well-structured, with good practices like AbortController for cleanup and clear error handling in the proxy. However, the PR has two significant weaknesses: a complete lack of automated tests for the new API endpoint, and a brittle, duplicated type definition in the API proxy that undermines type safety and maintainability. Several smaller improvements around component composition, error message specificity, and code style are also recommended. The inclusion of unrelated Gemini API code also unnecessarily broadens the scope of this PR. The PR is a good foundation but requires addressing the testing and typing issues before it can be considered production-ready.

❌ Rules Violated (3)1. No console.log statements Medium

🟡 Code should not contain console.log statements in production
📝 Found 2 occurrence(s) in packages/api/src/index.ts
📁 Files: packages/api/src/index.ts

  1. No TODO comments Low
    🔵 TODO comments should be resolved before merging
    📝 Found 1 occurrence(s) in packages/api/src/index.ts
    📁 Files: packages/api/src/index.ts

  2. Test coverage High
    🔴 New code should have adequate test coverage
    📝 Found 3 code file(s) but no test files
    📁 Files: packages/api/src/index.ts, packages/mobile/pages/BountyDetail.tsx, packages/mobile/types.ts

💡 Code Suggestions (6)

🔴 High Priority (2)

  1. packages/api/src/index.ts (Line 127)
    🔧 The response data from the upstream API is cast using a large, inline as { ... } type. This is brittle and unsafely duplicates the Bounty type from the frontend. This should be replaced with a shared type definition to ensure type safety and maintainability.

💭 Reasoning: Duplicating types across packages leads to maintenance overhead and potential inconsistencies. If the upstream API or the frontend type changes, this inline type will not be updated automatically, leading to silent runtime errors that TypeScript cannot catch. A shared packages/types would solve this.

Suggested Code:

const data = await response.json() as Bounty;
  1. packages/api/src/index.ts
    ✨ The new /api/bounties/:id endpoint lacks any automated tests. Add an integration test suite for this endpoint to verify correct behavior for valid IDs, invalid IDs (400), non-existent IDs (404), and upstream API failures (502).

💭 Reasoning: Automated tests are essential for ensuring the reliability of the API and preventing regressions. The manual curl commands in the PR description are a good start but are not a substitute for a repeatable, automated test suite that can be run in CI.

🟡 Medium Priority (1)

  1. packages/mobile/pages/BountyDetail.tsx (Line 110)
    ✨ The BountyDetail component is over 300 lines and manages multiple distinct UI views (details, application form, submission confirmation). To improve readability and maintainability, extract the application form (the if (applying) block) and the submission confirmation screen (the if (submitted) block) into their own separate components.

💭 Reasoning: Breaking down large components into smaller, focused ones is a core principle of React development. It makes the code easier to read, test, and reuse, and it isolates state and logic related to specific UI parts.

🔵 Low Priority (3)

  1. packages/api/src/index.ts (Line 117)
    🎨 The construction of the Authorization header uses a ternary with a non-null assertion (!). This can be simplified and made safer using conditional property spreading.

💭 Reasoning: This approach is more idiomatic and avoids the non-null assertion, which can suppress legitimate type errors. It makes the intent clearer and is generally considered a safer pattern for conditionally adding properties to an object.

Suggested Code:

const authHeader = c.req.header('Authorization');
// ...
headers: {
    'Content-Type': 'application/json',
    ...(authHeader && { Authorization: authHeader }),
},
  1. packages/mobile/pages/BountyDetail.tsx (Line 56)
    ✨ The error handling in the fetchBounty function sets a generic error message. This can be improved by attempting to parse a JSON error message from the API response, providing more specific feedback to the user (e.g., 'Bounty not found' from a 404 response).

💭 Reasoning: Displaying specific error messages from the API provides a better user experience and makes debugging easier. A generic 'Failed to load' message can be frustrating for users and developers alike.

Suggested Code:

if (!response.ok) {
  let errorMessage = `Server error: ${response.status}`;
  try {
    const errorData = await response.json();
    if (errorData.error) errorMessage = errorData.error;
  } catch {}
  throw new Error(errorMessage);
}
  1. packages/api/src/index.ts (Line 9)
    ✨ The index.ts file includes setup and an endpoint for a Gemini API, which is unrelated to the PR's stated goal of implementing the bounty detail endpoint. This adds noise to the review and increases the scope of the change. Consider moving this unrelated logic to a separate PR.

💭 Reasoning: Pull requests should be small and focused on a single, atomic change. This makes them easier to review, test, and merge, reducing the risk of introducing unrelated bugs.

📊 Review Metadata
  • Processing Time: 162s
  • Analysis Date: 2/18/2026, 6:26:28 AM

🤖 This review was generated by AI. While we strive for accuracy, please use your judgment when applying suggestions.

💬 Questions about this review? Open an issue or contact support.

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.

Implement GET /bounties/:id — bounty detail

2 participants