feat(frontend): add next.tags cache support to apiFetch; tag all GET fetches
This commit is contained in:
@@ -151,9 +151,13 @@ const API_BASE_URL =
|
|||||||
? process.env.NEXT_PUBLIC_SERVER_SIDE_API_URL
|
? process.env.NEXT_PUBLIC_SERVER_SIDE_API_URL
|
||||||
: process.env.NEXT_PUBLIC_API_URL;
|
: process.env.NEXT_PUBLIC_API_URL;
|
||||||
|
|
||||||
|
type ApiFetchOptions = Omit<RequestInit, 'next'> & {
|
||||||
|
next?: { tags?: string[]; revalidate?: number | false }
|
||||||
|
}
|
||||||
|
|
||||||
async function apiFetch<T>(
|
async function apiFetch<T>(
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
options: RequestInit = {},
|
options: ApiFetchOptions = {},
|
||||||
schema: z.ZodType<T>,
|
schema: z.ZodType<T>,
|
||||||
token?: string | null
|
token?: string | null
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
@@ -161,9 +165,11 @@ async function apiFetch<T>(
|
|||||||
throw new Error("API_BASE_URL is not defined");
|
throw new Error("API_BASE_URL is not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { next, ...restOptions } = options;
|
||||||
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
...(options.headers as Record<string, string>),
|
...(restOptions.headers as Record<string, string>),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
@@ -171,8 +177,9 @@ async function apiFetch<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
||||||
...options,
|
...restOptions,
|
||||||
headers,
|
headers,
|
||||||
|
...(next ? { next } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -202,7 +209,7 @@ export const loginUser = (data: z.infer<typeof LoginSchema>) =>
|
|||||||
// ── Current user ──────────────────────────────────────────────────────────
|
// ── Current user ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export const getMe = (token: string) =>
|
export const getMe = (token: string) =>
|
||||||
apiFetch("/users/me", {}, MeSchema, token);
|
apiFetch("/users/me", { next: { tags: ['me'] } }, MeSchema, token);
|
||||||
|
|
||||||
export const updateProfile = (data: z.infer<typeof UpdateProfileSchema>, token: string) =>
|
export const updateProfile = (data: z.infer<typeof UpdateProfileSchema>, token: string) =>
|
||||||
apiFetch("/users/me", { method: "PATCH", body: JSON.stringify(data) }, UserSchema, token);
|
apiFetch("/users/me", { method: "PATCH", body: JSON.stringify(data) }, UserSchema, token);
|
||||||
@@ -213,13 +220,13 @@ export const getMeFollowingList = (token: string) =>
|
|||||||
// ── Users ─────────────────────────────────────────────────────────────────
|
// ── Users ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export const getUserProfile = (username: string, token: string | null) =>
|
export const getUserProfile = (username: string, token: string | null) =>
|
||||||
apiFetch(`/users/${username}`, {}, UserSchema, token);
|
apiFetch(`/users/${username}`, { next: { tags: [`profile:${username}`] } }, UserSchema, token);
|
||||||
|
|
||||||
export const getFollowersList = (username: string, token: string | null) =>
|
export const getFollowersList = (username: string, token: string | null) =>
|
||||||
apiFetch(`/users/${username}/followers`, {}, z.object({ total: z.number(), items: z.array(UserSchema) }), token);
|
apiFetch(`/users/${username}/followers`, { next: { tags: [`profile:${username}`] } }, z.object({ total: z.number(), items: z.array(UserSchema) }), token);
|
||||||
|
|
||||||
export const getFollowingList = (username: string, token: string | null) =>
|
export const getFollowingList = (username: string, token: string | null) =>
|
||||||
apiFetch(`/users/${username}/following`, {}, z.object({ total: z.number(), items: z.array(UserSchema) }), token);
|
apiFetch(`/users/${username}/following`, { next: { tags: [`profile:${username}`] } }, z.object({ total: z.number(), items: z.array(UserSchema) }), token);
|
||||||
|
|
||||||
export const getTopFriends = (username: string, token: string | null) =>
|
export const getTopFriends = (username: string, token: string | null) =>
|
||||||
apiFetch(
|
apiFetch(
|
||||||
@@ -330,7 +337,7 @@ export const getAllUsersCount = () =>
|
|||||||
export const getFeed = (token: string, page: number = 1, pageSize: number = 20) =>
|
export const getFeed = (token: string, page: number = 1, pageSize: number = 20) =>
|
||||||
apiFetch(
|
apiFetch(
|
||||||
`/feed?page=${page}&per_page=${pageSize}`,
|
`/feed?page=${page}&per_page=${pageSize}`,
|
||||||
{},
|
{ next: { tags: ['feed'] } },
|
||||||
z.object({ items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() })
|
z.object({ items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() })
|
||||||
.transform((d) => ({ ...d, totalPages: Math.ceil(d.total / d.per_page) })),
|
.transform((d) => ({ ...d, totalPages: Math.ceil(d.total / d.per_page) })),
|
||||||
token
|
token
|
||||||
@@ -339,7 +346,7 @@ export const getFeed = (token: string, page: number = 1, pageSize: number = 20)
|
|||||||
export const getUserThoughts = (username: string, token: string | null) =>
|
export const getUserThoughts = (username: string, token: string | null) =>
|
||||||
apiFetch(
|
apiFetch(
|
||||||
`/users/${username}/thoughts`,
|
`/users/${username}/thoughts`,
|
||||||
{},
|
{ next: { tags: [`profile:${username}`] } },
|
||||||
z.object({ items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() }),
|
z.object({ items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() }),
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
@@ -351,7 +358,7 @@ export const deleteThought = (thoughtId: string, token: string) =>
|
|||||||
apiFetch(`/thoughts/${thoughtId}`, { method: "DELETE" }, z.null(), token);
|
apiFetch(`/thoughts/${thoughtId}`, { method: "DELETE" }, z.null(), token);
|
||||||
|
|
||||||
export const getThoughtById = (thoughtId: string, token: string | null) =>
|
export const getThoughtById = (thoughtId: string, token: string | null) =>
|
||||||
apiFetch(`/thoughts/${thoughtId}`, {}, ThoughtSchema, token);
|
apiFetch(`/thoughts/${thoughtId}`, { next: { tags: [`thought:${thoughtId}`] } }, ThoughtSchema, token);
|
||||||
|
|
||||||
export const getThoughtThread = async (thoughtId: string, token: string | null): Promise<ThoughtThread> => {
|
export const getThoughtThread = async (thoughtId: string, token: string | null): Promise<ThoughtThread> => {
|
||||||
const thoughts = await apiFetch(`/thoughts/${thoughtId}/thread`, {}, z.array(ThoughtSchema), token);
|
const thoughts = await apiFetch(`/thoughts/${thoughtId}/thread`, {}, z.array(ThoughtSchema), token);
|
||||||
@@ -375,7 +382,7 @@ export const getThoughtThread = async (thoughtId: string, token: string | null):
|
|||||||
export const getThoughtsByTag = (tagName: string, token: string | null) =>
|
export const getThoughtsByTag = (tagName: string, token: string | null) =>
|
||||||
apiFetch(
|
apiFetch(
|
||||||
`/tags/${tagName}`,
|
`/tags/${tagName}`,
|
||||||
{},
|
{ next: { tags: [`tag:${tagName}`, 'feed'] } },
|
||||||
z.object({ tag: z.string(), items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() }),
|
z.object({ tag: z.string(), items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() }),
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
@@ -391,7 +398,7 @@ export const getPopularTags = () =>
|
|||||||
// ── Search ────────────────────────────────────────────────────────────────
|
// ── Search ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export const search = (query: string, token: string | null) =>
|
export const search = (query: string, token: string | null) =>
|
||||||
apiFetch(`/search?q=${encodeURIComponent(query)}`, {}, SearchResultsSchema, token);
|
apiFetch(`/search?q=${encodeURIComponent(query)}`, { next: { tags: ['search'] } }, SearchResultsSchema, token);
|
||||||
|
|
||||||
// ── API Keys ──────────────────────────────────────────────────────────────
|
// ── API Keys ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user