Preserve alias casing in chat fetch query by foqc · Pull Request #1696 · EvolutionAPI/evolution-api · GitHub
Skip to content

Preserve alias casing in chat fetch query#1696

Merged
DavidsonGomes merged 4 commits intoEvolutionAPI:developfrom
foqc:foqc/develop
Jul 21, 2025
Merged

Preserve alias casing in chat fetch query#1696
DavidsonGomes merged 4 commits intoEvolutionAPI:developfrom
foqc:foqc/develop

Conversation

@foqc
Copy link
Copy Markdown
Contributor

@foqc foqc commented Jul 7, 2025

Bug Fix: Inconsistent Column Alias Casing in fetchChats

Bug description: #1691

Context

In PostgreSQL, unquoted column aliases are automatically lowercased. In our channel.service.ts → fetchChats query, there was a mix of quoted and unquoted aliases, causing PostgreSQL to return fields like updatedat and windowstart instead of the expected updatedAt and windowStart.
This inconsistency broke or skipped field mappings in the application layer.

Additionally, the pagination logic was applied twice due to redundant filtering, which led to incorrect result sets when paginating chat data.


Changes Made

  • Quoted all column aliases consistently in the SQL query to preserve camelCase casing.
  • Removed duplicate pagination filter, ensuring correct OFFSET and LIMIT behavior for chat pagination.

Affected Environments

  • Evolution API: v2.3.0 (main branch)
  • Node.js: v20
  • Database: PostgreSQL (Evolution schema)
  • Caching: Redis
  • Deployed via Docker

Outcome

  • Chat fetch mappings now work correctly, returning all fields in expected casing.
  • Pagination is accurate, with no overlapping or missing items across pages.

Summary by Sourcery

Fix inconsistent column alias casing and eliminate duplicate pagination logic in the chat fetch query to restore proper field mappings and accurate paging.

Bug Fixes:

  • Quote all column aliases in the fetchChats SQL query to preserve expected camelCase field names
  • Remove redundant pagination filtering to ensure correct OFFSET and LIMIT behavior for chat data

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jul 7, 2025

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey @foqc - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 773 to 806
const mappedResults = results.map((contact) => {
const lastMessage = contact.lastmessageid
const lastMessage = contact.lastMessageId
? {
id: contact.lastmessageid,
key: contact.lastmessage_key,
pushName: contact.lastmessagepushname,
participant: contact.lastmessageparticipant,
messageType: contact.lastmessagemessagetype,
message: contact.lastmessagemessage,
contextInfo: contact.lastmessagecontextinfo,
source: contact.lastmessagesource,
messageTimestamp: contact.lastmessagemessagetimestamp,
instanceId: contact.lastmessageinstanceid,
sessionId: contact.lastmessagesessionid,
status: contact.lastmessagestatus,
id: contact.lastMessageId,
key: contact.lastMessage_key,
pushName: contact.lastMessagePushName,
participant: contact.lastMessageParticipant,
messageType: contact.lastMessageMessageType,
message: contact.lastMessageMessage,
contextInfo: contact.lastMessageContextInfo,
source: contact.lastMessageSource,
messageTimestamp: contact.lastMessageMessageTimestamp,
instanceId: contact.lastMessageInstanceId,
sessionId: contact.lastMessageSessionId,
status: contact.lastMessageStatus,
}
: undefined;

return {
id: contact.contactid || null,
remoteJid: contact.remotejid,
pushName: contact.pushname,
profilePicUrl: contact.profilepicurl,
updatedAt: contact.updatedat,
windowStart: contact.windowstart,
windowExpires: contact.windowexpires,
windowActive: contact.windowactive,
id: contact.contactId || null,
remoteJid: contact.remoteJid,
pushName: contact.pushName,
profilePicUrl: contact.profilePicUrl,
updatedAt: contact.updatedAt,
windowStart: contact.windowStart,
windowExpires: contact.windowExpires,
windowActive: contact.windowActive,
lastMessage: lastMessage ? this.cleanMessageData(lastMessage) : undefined,
unreadCount: 0,
isSaved: !!contact.contactid,
unreadCount: contact.unreadMessages,
isSaved: !!contact.contactId,
};
});

if (query?.take && query?.skip) {
const skip = query.skip || 0;
const take = query.take || 20;
return mappedResults.slice(skip, skip + take);
}

return mappedResults;
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.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
const mappedResults = results.map((contact) => {
const lastMessage = contact.lastmessageid
const lastMessage = contact.lastMessageId
? {
id: contact.lastmessageid,
key: contact.lastmessage_key,
pushName: contact.lastmessagepushname,
participant: contact.lastmessageparticipant,
messageType: contact.lastmessagemessagetype,
message: contact.lastmessagemessage,
contextInfo: contact.lastmessagecontextinfo,
source: contact.lastmessagesource,
messageTimestamp: contact.lastmessagemessagetimestamp,
instanceId: contact.lastmessageinstanceid,
sessionId: contact.lastmessagesessionid,
status: contact.lastmessagestatus,
id: contact.lastMessageId,
key: contact.lastMessage_key,
pushName: contact.lastMessagePushName,
participant: contact.lastMessageParticipant,
messageType: contact.lastMessageMessageType,
message: contact.lastMessageMessage,
contextInfo: contact.lastMessageContextInfo,
source: contact.lastMessageSource,
messageTimestamp: contact.lastMessageMessageTimestamp,
instanceId: contact.lastMessageInstanceId,
sessionId: contact.lastMessageSessionId,
status: contact.lastMessageStatus,
}
: undefined;
return {
id: contact.contactid || null,
remoteJid: contact.remotejid,
pushName: contact.pushname,
profilePicUrl: contact.profilepicurl,
updatedAt: contact.updatedat,
windowStart: contact.windowstart,
windowExpires: contact.windowexpires,
windowActive: contact.windowactive,
id: contact.contactId || null,
remoteJid: contact.remoteJid,
pushName: contact.pushName,
profilePicUrl: contact.profilePicUrl,
updatedAt: contact.updatedAt,
windowStart: contact.windowStart,
windowExpires: contact.windowExpires,
windowActive: contact.windowActive,
lastMessage: lastMessage ? this.cleanMessageData(lastMessage) : undefined,
unreadCount: 0,
isSaved: !!contact.contactid,
unreadCount: contact.unreadMessages,
isSaved: !!contact.contactId,
};
});
if (query?.take && query?.skip) {
const skip = query.skip || 0;
const take = query.take || 20;
return mappedResults.slice(skip, skip + take);
}
return mappedResults;
return results.map((contact) => {
const lastMessage = contact.lastMessageId
? {
id: contact.lastMessageId,
key: contact.lastMessage_key,
pushName: contact.lastMessagePushName,
participant: contact.lastMessageParticipant,
messageType: contact.lastMessageMessageType,
message: contact.lastMessageMessage,
contextInfo: contact.lastMessageContextInfo,
source: contact.lastMessageSource,
messageTimestamp: contact.lastMessageMessageTimestamp,
instanceId: contact.lastMessageInstanceId,
sessionId: contact.lastMessageSessionId,
status: contact.lastMessageStatus,
}
: undefined;
return {
id: contact.contactId || null,
remoteJid: contact.remoteJid,
pushName: contact.pushName,
profilePicUrl: contact.profilePicUrl,
updatedAt: contact.updatedAt,
windowStart: contact.windowStart,
windowExpires: contact.windowExpires,
windowActive: contact.windowActive,
lastMessage: lastMessage ? this.cleanMessageData(lastMessage) : undefined,
unreadCount: contact.unreadMessages,
isSaved: !!contact.contactId,
};
});


ExplanationSomething that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.

@DavidsonGomes DavidsonGomes changed the base branch from main to develop July 14, 2025 17:46
@DavidsonGomes
Copy link
Copy Markdown
Collaborator

Please, fix the lint with npm run lint

@foqc
Copy link
Copy Markdown
Contributor Author

foqc commented Jul 15, 2025

@DavidsonGomes DavidsonGomes merged commit 0e358cf into EvolutionAPI:develop Jul 21, 2025
1 check passed
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.

2 participants