Skip to content

Commit 225691d

Browse files
committed
feat: add optimized notes counts endpoint for mobile app
- add GET /api/notes/counts endpoint that returns aggregated note counts (all, starred, archived, trash) without fetching full note data. Includes Valkey caching with 2-minute TTL for improved performance.
1 parent 2e99465 commit 225691d

File tree

4 files changed

+64
-4
lines changed

4 files changed

+64
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
## [1.6.2](https://github.com/typelets/typelets-api/compare/v1.6.1...v1.6.2) (2025-10-15)
22

3-
43
### Bug Fixes
54

6-
* replace GITHUB_TOKEN with PAT_TOKEN in release workflow ([6327365](https://github.com/typelets/typelets-api/commit/632736522b75a68d0e268d0a649d5ec6b5367db3))
7-
* replace GITHUB_TOKEN with PAT_TOKEN in release workflow ([844f9d9](https://github.com/typelets/typelets-api/commit/844f9d997391bda0ffe4b7788efa019cddf5d982))
5+
- replace GITHUB_TOKEN with PAT_TOKEN in release workflow ([6327365](https://github.com/typelets/typelets-api/commit/632736522b75a68d0e268d0a649d5ec6b5367db3))
6+
- replace GITHUB_TOKEN with PAT_TOKEN in release workflow ([844f9d9](https://github.com/typelets/typelets-api/commit/844f9d997391bda0ffe4b7788efa019cddf5d982))
87

98
## [1.6.1](https://github.com/typelets/typelets-api/compare/v1.6.0...v1.6.1) (2025-10-15)
109

src/lib/cache-keys.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const CacheKeys = {
1515
notesArchived: (userId: string) => `notes:${userId}:archived`,
1616
notesDeleted: (userId: string) => `notes:${userId}:deleted`,
1717
notesDeletedCount: (userId: string) => `notes:${userId}:deletedCount`,
18+
notesCounts: (userId: string) => `notes:${userId}:counts`,
1819

1920
// Attachment-related
2021
noteAttachments: (noteId: string) => `attachments:note:${noteId}`,
@@ -31,5 +32,6 @@ export const CacheTTL = {
3132
notesStarred: 300, // 5 minutes
3233
notesArchived: 300, // 5 minutes
3334
notesDeleted: 300, // 5 minutes
35+
notesCounts: 120, // 2 minutes
3436
noteAttachments: 1800, // 30 minutes
3537
} as const;

src/routes/notes.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { db, notes } from "../db";
55
import { createNoteSchema, updateNoteSchema, notesQuerySchema } from "../lib/validation";
66
import { eq, and, desc, or, ilike, count, SQL } from "drizzle-orm";
77
import { checkNoteLimits } from "../middleware/usage";
8+
import { getCache, setCache } from "../lib/cache";
9+
import { CacheKeys, CacheTTL } from "../lib/cache-keys";
810

911
const notesRouter = new Hono();
1012

@@ -79,6 +81,63 @@ notesRouter.get("/", zValidator("query", notesQuerySchema), async (c) => {
7981
});
8082
});
8183

84+
notesRouter.get("/counts", async (c) => {
85+
const userId = c.get("userId");
86+
const cacheKey = CacheKeys.notesCounts(userId);
87+
88+
// Try to get from cache first
89+
const cachedCounts = await getCache<{
90+
all: number;
91+
starred: number;
92+
archived: number;
93+
trash: number;
94+
}>(cacheKey);
95+
96+
if (cachedCounts) {
97+
return c.json(cachedCounts);
98+
}
99+
100+
// If not in cache, query the database
101+
const [allCount] = await db
102+
.select({ total: count() })
103+
.from(notes)
104+
.where(and(eq(notes.userId, userId), eq(notes.deleted, false), eq(notes.archived, false)));
105+
106+
const [starredCount] = await db
107+
.select({ total: count() })
108+
.from(notes)
109+
.where(
110+
and(
111+
eq(notes.userId, userId),
112+
eq(notes.starred, true),
113+
eq(notes.deleted, false),
114+
eq(notes.archived, false)
115+
)
116+
);
117+
118+
const [archivedCount] = await db
119+
.select({ total: count() })
120+
.from(notes)
121+
.where(and(eq(notes.userId, userId), eq(notes.archived, true), eq(notes.deleted, false)));
122+
123+
const [trashCount] = await db
124+
.select({ total: count() })
125+
.from(notes)
126+
.where(and(eq(notes.userId, userId), eq(notes.deleted, true)));
127+
128+
const counts = {
129+
all: allCount.total,
130+
starred: starredCount.total,
131+
archived: archivedCount.total,
132+
trash: trashCount.total,
133+
};
134+
135+
// Cache the results
136+
await setCache(cacheKey, counts, CacheTTL.notesCounts);
137+
138+
return c.json(counts);
139+
});
140+
82141
notesRouter.get("/:id", async (c) => {
83142
const userId = c.get("userId");
84143
const noteId = c.req.param("id");

src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
// This file is automatically updated by semantic-release
2-
export const VERSION = "1.6.2"
2+
export const VERSION = "1.6.2";

0 commit comments

Comments
 (0)