Skip to content

Commit 8536c43

Browse files
committed
wrap the mongoDbClient in react cache()
1 parent 1a61d33 commit 8536c43

File tree

1 file changed

+90
-87
lines changed

1 file changed

+90
-87
lines changed

src/clients/mongodb/index.ts

+90-87
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,112 @@
11
import { Document, MongoClient } from 'mongodb'
22
import { unstable_cache, unstable_expireTag } from 'next/cache'
3+
import { cache } from 'react'
34
import { z } from 'zod'
45
import { Channel } from '../../models/channel'
56
import { Video } from '../../models/video'
67
import { DbClient, dbClientSchema } from '../../schemas/db-client'
78

8-
export const createMongoDbClient = z
9-
.function()
10-
.args(z.object({ connectionString: z.string().min(1) }))
11-
.returns(z.promise(dbClientSchema as z.ZodType<DbClient>))
12-
.implement(async function createMongoDbClient({ connectionString }): Promise<DbClient> {
13-
const databaseName = 'youtube-serverless'
9+
export const createMongoDbClient = cache(
10+
z
11+
.function()
12+
.args(z.object({ connectionString: z.string().min(1) }))
13+
.returns(z.promise(dbClientSchema as z.ZodType<DbClient>))
14+
.implement(async function createMongoDbClient({ connectionString }): Promise<DbClient> {
15+
const databaseName = 'youtube-serverless'
1416

15-
// Maintain the same client across factory calls, as long as the connection string is the same.
16-
const client = new MongoClient(connectionString, { appName: 'youtube-serverless' })
17+
// Maintain the same client across factory calls, as long as the connection string is the same.
18+
const client = new MongoClient(connectionString, { appName: 'youtube-serverless' })
1719

18-
type Collection = 'channels' | 'videos'
20+
type Collection = 'channels' | 'videos'
1921

20-
const getCollection = <C extends Document = never>(collectionName: Collection) => {
21-
const collection = client.db(databaseName).collection<C>(collectionName)
22+
const getCollection = <C extends Document = never>(collectionName: Collection) => {
23+
const collection = client.db(databaseName).collection<C>(collectionName)
2224

23-
return Promise.resolve({
24-
collection,
25-
})
26-
}
27-
28-
const getChannels = dbClientSchema.shape.getChannels.implement(
29-
async function getChannels(): Promise<Channel[]> {
30-
const { collection } = await getCollection<Channel>('channels')
31-
const channelsWithId = await collection.find().toArray()
32-
return channelsWithId.map<Channel>(
33-
(channel) =>
34-
({
35-
channelId: channel.channelId,
36-
channelTitle: channel.channelTitle,
37-
playlist: channel.playlist,
38-
thumbnail: channel.thumbnail,
39-
channelThumbnail: channel.channelThumbnail,
40-
channelLink: channel.channelLink,
41-
}) satisfies Channel
42-
)
25+
return Promise.resolve({
26+
collection,
27+
})
4328
}
44-
)
4529

46-
const updateChannel = dbClientSchema.shape.updateChannel.implement(
47-
async function updateChannel({ channel }) {
48-
const { collection } = await getCollection<Channel>('channels')
49-
await collection.replaceOne({ channelId: channel.channelId }, channel, { upsert: true })
50-
}
51-
)
30+
const getChannels = dbClientSchema.shape.getChannels.implement(
31+
async function getChannels(): Promise<Channel[]> {
32+
const { collection } = await getCollection<Channel>('channels')
33+
const channelsWithId = await collection.find().toArray()
34+
return channelsWithId.map<Channel>(
35+
(channel) =>
36+
({
37+
channelId: channel.channelId,
38+
channelTitle: channel.channelTitle,
39+
playlist: channel.playlist,
40+
thumbnail: channel.thumbnail,
41+
channelThumbnail: channel.channelThumbnail,
42+
channelLink: channel.channelLink,
43+
}) satisfies Channel
44+
)
45+
}
46+
)
5247

53-
const latestVideosTag = 'latest-videos'
48+
const updateChannel = dbClientSchema.shape.updateChannel.implement(
49+
async function updateChannel({ channel }) {
50+
const { collection } = await getCollection<Channel>('channels')
51+
await collection.replaceOne({ channelId: channel.channelId }, channel, { upsert: true })
52+
}
53+
)
5454

55-
const putLatestVideos = dbClientSchema.shape.putLatestVideos.implement(async function putVideo({
56-
videos,
57-
}) {
58-
const { collection } = await getCollection<{ videos: Video[] }>('videos')
59-
await collection.updateOne({}, { $set: { videos } }, { upsert: true })
60-
unstable_expireTag(latestVideosTag)
61-
})
55+
const latestVideosTag = 'latest-videos'
6256

63-
const getLatestVideos = dbClientSchema.shape.getLatestVideos.implement(
64-
unstable_cache(
65-
async function getLatestVideos({ limit }): Promise<Video[]> {
57+
const putLatestVideos = dbClientSchema.shape.putLatestVideos.implement(
58+
async function putVideo({ videos }) {
6659
const { collection } = await getCollection<{ videos: Video[] }>('videos')
67-
const { videos } = (await collection.findOne()) ?? {}
68-
return videos == null
69-
? []
70-
: videos.slice(0, limit).map<Video>(
71-
(video) =>
72-
({
73-
channelId: video.channelId,
74-
videoId: video.videoId,
75-
videoPublishedAt: video.videoPublishedAt,
76-
thumbnail: video.thumbnail,
77-
channelTitle: video.channelTitle,
78-
channelThumbnail: video.channelThumbnail,
79-
channelLink: video.channelLink,
80-
title: video.title,
81-
durationInSeconds: video.durationInSeconds,
82-
}) satisfies Video
83-
)
84-
},
85-
['latest-videos'],
86-
{ revalidate: 60 * 60, tags: [latestVideosTag] }
60+
await collection.updateOne({}, { $set: { videos } }, { upsert: true })
61+
unstable_expireTag(latestVideosTag)
62+
}
8763
)
88-
)
8964

90-
const deleteOldVideos = dbClientSchema.shape.deleteOldVideos.implement(
91-
async function deleteOldVideos(): Promise<number> {
92-
// NOTE: Nothing to cleanup for this backend.
93-
return Promise.resolve(0)
94-
}
95-
)
65+
const getLatestVideos = dbClientSchema.shape.getLatestVideos.implement(
66+
unstable_cache(
67+
async function getLatestVideos({ limit }): Promise<Video[]> {
68+
const { collection } = await getCollection<{ videos: Video[] }>('videos')
69+
const { videos } = (await collection.findOne()) ?? {}
70+
return videos == null
71+
? []
72+
: videos.slice(0, limit).map<Video>(
73+
(video) =>
74+
({
75+
channelId: video.channelId,
76+
videoId: video.videoId,
77+
videoPublishedAt: video.videoPublishedAt,
78+
thumbnail: video.thumbnail,
79+
channelTitle: video.channelTitle,
80+
channelThumbnail: video.channelThumbnail,
81+
channelLink: video.channelLink,
82+
title: video.title,
83+
durationInSeconds: video.durationInSeconds,
84+
}) satisfies Video
85+
)
86+
},
87+
['latest-videos'],
88+
{ revalidate: 60 * 60, tags: [latestVideosTag] }
89+
)
90+
)
9691

97-
const close: DbClient['close'] = () => client.close()
92+
const deleteOldVideos = dbClientSchema.shape.deleteOldVideos.implement(
93+
async function deleteOldVideos(): Promise<number> {
94+
// NOTE: Nothing to cleanup for this backend.
95+
return Promise.resolve(0)
96+
}
97+
)
98+
99+
const close: DbClient['close'] = () => client.close()
98100

99-
await client.connect()
101+
await client.connect()
100102

101-
return {
102-
getChannels,
103-
updateChannel,
104-
putLatestVideos,
105-
getLatestVideos,
106-
deleteOldVideos,
107-
close,
108-
} satisfies DbClient
109-
})
103+
return {
104+
getChannels,
105+
updateChannel,
106+
putLatestVideos,
107+
getLatestVideos,
108+
deleteOldVideos,
109+
close,
110+
} satisfies DbClient
111+
})
112+
)

0 commit comments

Comments
 (0)