feat: add GET /api/nfc/payload endpoint for NFC tag payload generation#67
Open
SdSarthak wants to merge 2 commits into
Open
feat: add GET /api/nfc/payload endpoint for NFC tag payload generation#67SdSarthak wants to merge 2 commits into
SdSarthak wants to merge 2 commits into
Conversation
- Add apps/backend/src/routes/nfc.ts with authenticated GET /api/nfc/payload that returns an NDEF-compatible URI record for the user's profile page - Optional ?card=<cardId> query param returns card-specific URL after validating that the card belongs to the requesting user (403 on failure) - Register nfcRoutes in app.ts at prefix /api/nfc - Add unit tests covering: 401 unauthenticated, profile URL, card URL, 403 on unowned card Closes Dev-Card#35
There was a problem hiding this comment.
Pull request overview
Adds an authenticated backend endpoint to generate an NFC/NDEF-compatible URI payload pointing to either the requesting user’s profile URL or a specific owned card URL, and wires it into the backend app with unit tests.
Changes:
- Added
GET /api/nfc/payload(JWT-protected) to return{ type: 'URI', payload: '<public URL>' }for profile or card. - Registered the new
/api/nfcroute group in the Fastify app. - Added Vitest coverage for unauthenticated access, profile payload, card payload, and unowned card access.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| apps/backend/src/routes/nfc.ts | Implements the NFC payload generation endpoint with card ownership validation. |
| apps/backend/src/app.ts | Registers the new nfcRoutes under /api/nfc. |
| apps/backend/src/tests/nfc.test.ts | Adds unit tests for auth, default profile payload, card payload, and forbidden access. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+11
to
+15
| // Fetch user to get username for the profile URL | ||
| const user = await app.prisma.user.findUnique({ | ||
| where: { id: userId }, | ||
| select: { username: true }, | ||
| }); |
Comment on lines
+21
to
+22
| const appUrl = process.env.PUBLIC_APP_URL || 'https://devcard.dev'; | ||
|
|
Comment on lines
+35
to
+51
| const app = buildApp(); | ||
| await app.ready(); | ||
| const res = await app.inject({ method: 'GET', url: '/api/nfc/payload' }); | ||
| expect(res.statusCode).toBe(200); | ||
| const body = JSON.parse(res.body); | ||
| expect(body.type).toBe('URI'); | ||
| expect(body.payload).toContain('/u/testuser'); | ||
| }); | ||
|
|
||
| it('returns card URI payload when cardId is provided and owned', async () => { | ||
| const app = buildApp(); | ||
| await app.ready(); | ||
| const res = await app.inject({ method: 'GET', url: '/api/nfc/payload?card=card-1' }); | ||
| expect(res.statusCode).toBe(200); | ||
| const body = JSON.parse(res.body); | ||
| expect(body.type).toBe('URI'); | ||
| expect(body.payload).toContain('/devcard/card-1'); |
Comment on lines
+35
to
+51
| const app = buildApp(); | ||
| await app.ready(); | ||
| const res = await app.inject({ method: 'GET', url: '/api/nfc/payload' }); | ||
| expect(res.statusCode).toBe(200); | ||
| const body = JSON.parse(res.body); | ||
| expect(body.type).toBe('URI'); | ||
| expect(body.payload).toContain('/u/testuser'); | ||
| }); | ||
|
|
||
| it('returns card URI payload when cardId is provided and owned', async () => { | ||
| const app = buildApp(); | ||
| await app.ready(); | ||
| const res = await app.inject({ method: 'GET', url: '/api/nfc/payload?card=card-1' }); | ||
| expect(res.statusCode).toBe(200); | ||
| const body = JSON.parse(res.body); | ||
| expect(body.type).toBe('URI'); | ||
| expect(body.payload).toContain('/devcard/card-1'); |
…set PUBLIC_APP_URL in test env
3 tasks
Author
|
All addressed in the follow-up commit:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GET /api/nfc/payloadendpoint that returns an NDEF-compatible URI record for NFC tag programming?card=<cardId>query param returns a card-specific URL; without it, returns the user's profile URL (/u/<username>)403if the card doesn't belong to the requesting userapp.authenticate(JWT-based auth)Test plan
GET /api/nfc/payloadwithout auth → 401GET /api/nfc/payload(authenticated, no query param) →{ type: "URI", payload: "https://devcard.dev/u/<username>" }GET /api/nfc/payload?card=<owned-card-id>→{ type: "URI", payload: "https://devcard.dev/devcard/<cardId>" }GET /api/nfc/payload?card=<other-user-card-id>→ 403pnpm test— all new tests passCloses #35