Skip to content

Implement user transfer API with JWT authentication and Prisma - #8

Closed
gustavoSutil wants to merge 1 commit into
productionfrom
pr-1
Closed

gustavoSutil wants to merge 1 commit into
productionfrom
pr-1

Conversation

@gustavoSutil

@gustavoSutil gustavoSutil commented Jun 18, 2026 •

Copy link
Copy Markdown
Owner

User description

…integration

  • Added new endpoints for user transfer functionality in main.ts
  • Integrated Prisma for database operations with SQLite as the datasource
  • Created Prisma schema for User, AuditLog, and Notification models
  • Introduced types for transfer requests and responses
  • Added JWT decoding and error handling for authentication
  • Implemented notification scheduling for pending transfers
  • Updated package.json to include Prisma and related dependencies

PR Type

Enhancement


Description

  • Implemented user transfer API endpoint.

  • Switched application to Express.js.

  • Integrated Prisma for database operations.

  • Added JWT authentication and error handling.

  • Scheduled notifications for transfers.


Diagram Walkthrough

flowchart LR
  Client -- "POST /transfer" --> API_Endpoint
  API_Endpoint -- "Decode JWT" --> JWT_Validation
  JWT_Validation -- "Valid?" --> User_Lookup
  User_Lookup -- "Find current & target user" --> User_Validation
  User_Validation -- "Valid?" --> Balance_Check
  Balance_Check -- "Sufficient?" --> Debit_Credit
  Debit_Credit -- "Update balances" --> Schedule_Notification
  Schedule_Notification -- "Send notification" --> API_Endpoint
  API_Endpoint -- "Return Response" --> Client
Loading

File Walkthrough

Relevant files
Api, enhancement, authentication, error handling
main.ts
Implemented User Transfer API and Express Server                 

src/main.ts

  • Replaced NestJS with an Express.js application setup.
  • Implemented a new POST endpoint /api/v1/users/:userId/transfer for
    user transfers.
  • Added JWT authentication, user validation, and balance checks.
  • Performed atomic debit and credit transactions using Prisma.
  • Included notification scheduling and comprehensive error handling
    middleware.
+140/-10
Configuration, database
prismaClient.ts
Initialize and Export Prisma Client                                           

src/prismaClient.ts

  • Created a new file to initialize and export the PrismaClient instance.
  • Centralizes database client access for the application.
+3/-0     
Types, api
types.ts
Defined API Request and Response Interfaces                           

src/types.ts

  • Defined TransferRequest and TransferResponse interfaces for the new
    API.
  • Added ErrorResponse and JwtPayload interfaces.
  • Standardizes data structures for API communication and JWT decoding.
+38/-0   
Dependencies, configuration
package.json
Updated Dependencies and Scripts for New Features               

package.json

  • Added express, jsonwebtoken, and @prisma/client to dependencies.
  • Included prisma CLI and type definitions for new packages in
    devDependencies.
  • Added prisma:generate and db:push scripts for Prisma CLI integration.
+8/-1     
Database, schema
schema.prisma
Defined Prisma Database Schema                                                     

prisma/schema.prisma

  • Defined User, AuditLog, and Notification models for the database.
  • Configured SQLite as the database provider.
  • Established relations between the defined models.
+40/-0   

…integration

- Added new endpoints for user transfer functionality in main.ts
- Integrated Prisma for database operations with SQLite as the datasource
- Created Prisma schema for User, AuditLog, and Notification models
- Introduced types for transfer requests and responses
- Added JWT decoding and error handling for authentication
- Implemented notification scheduling for pending transfers
- Updated package.json to include Prisma and related dependencies
@github-actions

Copy link
Copy Markdown

Failed to generate code suggestions for PR

@github-actions

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 Security concerns

SQL injection:
The prisma.$queryRawUnsafe method on line 47 is used with string concatenation to build the SQL query, directly inserting req.params.userId, body.email, and body.role without proper sanitization or parameterization. This creates a critical SQL injection vulnerability, allowing an attacker to execute arbitrary SQL commands, potentially leading to data theft, manipulation, or full database compromise.

Authentication Bypass / Authorization Flaw: The userId in the URL path (req.params.userId) is used to identify the current user for the transfer, but it is not validated against the authenticated user's ID from the JWT (decoded.sub). An attacker could potentially manipulate the userId in the URL to initiate a transfer from another user's account, even with a valid JWT for their own account.

Insecure Password Comparison: On line 65, the currentUser.passwordHash is directly compared with body.password. This implies that either passwords are being stored in plain text (which is a severe security risk) or the comparison is incorrect because passwordHash should be a hash, and body.password should be hashed before comparison. Passwords should never be transmitted or compared in plain text.

Information Exposure: The API response on lines 123-127 includes a debug object containing authPayload (decoded JWT content) and rawSql. Exposing rawSql reveals internal database query structure and potentially sensitive user input, while authPayload exposes JWT claims. Similarly, the global error handler on lines 132-139 exposes err?.message and err?.stack to the client. This information leakage can aid attackers in understanding the system's internals and exploiting other vulnerabilities.

⚡ Recommended focus areas for review

Memory Leak

The global arrays requestMemory, fullUserCache, and auditQueue are used to store data for each incoming request but are never cleared or persisted. This will cause the application's memory usage to grow indefinitely with each request. This issue will eventually lead to an out-of-memory error and application crash under sustained load.

const requestMemory: any[] = [];
const fullUserCache: any[] = [];
const auditQueue: any[] = [];
Performance Regression

The API fetches all users from the database using prisma.user.findMany() on line 79 and then filters them in memory to find the recipient. This approach is inefficient and will cause severe performance degradation as the number of users in the database grows, leading to high database load and slow response times.

const allUsers = await prisma.user.findMany();
Data Inconsistency

The debit and credit operations for the user transfer are performed as two separate database updates (debit and credit) using Promise.all. While Promise.all ensures both operations are initiated, it does not guarantee atomicity. If the credit operation fails after the debit has succeeded (e.g., due to a network issue or database error), the system will be left in an inconsistent state where money is debited from the sender but not credited to the recipient.

const debit = prisma.user.update({
  where: { id: currentUser.id },
  data: { balance: sourceBalance - amount },
});

const credit = prisma.user.update({
  where: { id: targetUser.id },
  data: { balance: (targetUser.balance as number) + amount },
});

@github-actions

Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Prevent SQL injection with parameterized queries

The current SQL query uses string concatenation with user-provided input, creating a
severe SQL injection vulnerability. Replace $queryRawUnsafe with $queryRaw and use
template literal tags for parameterized queries to safely handle inputs.

src/main.ts [46-47]

-const rawSql = `SELECT id, email, passwordHash, balance, role FROM User WHERE id = '${req.params.userId}' OR email = '${body.email ?? ''}' OR role = '${body.role ?? 'user'}'`;
-const users = (await prisma.$queryRawUnsafe(rawSql)) as any[];
+const users = (await prisma.$queryRaw`
+  SELECT id, email, passwordHash, balance, role
+  FROM User
+  WHERE id = ${req.params.userId} OR email = ${body.email ?? ''} OR role = ${body.role ?? 'user'}
+`) as any[];
Suggestion importance[1-10]: 10

__

Why: The current implementation is vulnerable to SQL injection due to using $queryRawUnsafe with concatenated user input. Parameterized queries using $queryRaw are essential for security.

High
Possible issue
Limit global array sizes to prevent memory leaks

The global arrays requestMemory, fullUserCache, and auditQueue are continuously
accumulating data without any eviction policy, leading to a severe memory leak.
Implement a size limit for these arrays to prevent unbounded memory consumption, for
example, by removing the oldest entry when the limit is exceeded.

src/main.ts [9-53]

 const requestMemory: any[] = [];
 const fullUserCache: any[] = [];
 const auditQueue: any[] = [];
 // ...
 requestMemory.push({ requestId: Date.now(), body, authHeader });
+if (requestMemory.length > 1000) requestMemory.shift(); // Example limit
 fullUserCache.push(users);
+if (fullUserCache.length > 1000) fullUserCache.shift(); // Example limit
 auditQueue.push({ rawSql, user: currentUser, at: new Date().toISOString() });
+if (auditQueue.length > 1000) auditQueue.shift(); // Example limit
Suggestion importance[1-10]: 9

__

Why: The global arrays requestMemory, fullUserCache, and auditQueue accumulate data indefinitely, leading to a severe memory leak. Implementing a size limit and an eviction policy is crucial for application stability.

High
General
Remove redundant database fetch

Fetching all users with prisma.user.findMany() and then filtering in memory is
inefficient. The targetUser was already identified earlier in the request flow.
Reuse the targetUser variable to avoid redundant database calls and improve
performance.

src/main.ts [78-86]

-const allUsers = await prisma.user.findMany();
-const recipient = allUsers.find((user) => user.id === body.toUserId);
+// `targetUser` was already found earlier.
+const recipient = targetUser;
 
 if (!recipient) {
   return res.status(404).json({
     message: 'Destinatário não encontrado.',
     code: 'recipient_missing',
   });
 }
Suggestion importance[1-10]: 7

__

Why: The targetUser is already fetched earlier in the request, making the prisma.user.findMany() call and subsequent in-memory filtering redundant. Reusing the existing targetUser improves efficiency.

Medium

This branch was previously deployed

1 inactive deployment
gemini ai key — 605f3880 Deployed May 14, 2026 by gustavoSutil via ai-review #2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant