Skip to content

Commit 9966636

Browse files
committed
feat: use zod for validation
1 parent 43ff345 commit 9966636

File tree

8 files changed

+145
-264
lines changed

8 files changed

+145
-264
lines changed

apps/insights/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"superjson": "catalog:",
4747
"swr": "catalog:",
4848
"zod": "catalog:",
49-
"zod-validation-error": "catalog:"
49+
"zod-validation-error": "catalog:",
50+
"zod-search-params": "catalog:"
5051
},
5152
"devDependencies": {
5253
"@cprussin/eslint-config": "catalog:",
Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
1-
import { NextRequest, NextResponse } from "next/server";
1+
import { NextRequest } from "next/server";
22
import { stringify } from "superjson";
3-
4-
import { Cluster, parseCluster } from "../../../../../services/pyth";
3+
import { z } from "zod";
4+
import { parseSearchParams } from "zod-search-params";
5+
6+
import {
7+
Cluster,
8+
CLUSTER_NAMES,
9+
ClusterToName,
10+
toCluster,
11+
} from "../../../../../services/pyth";
512
import { getFeeds } from "../../../../../services/pyth/get-feeds";
613

714
export const GET = async (
815
request: NextRequest,
916
{ params }: { params: Promise<{ publisher: string }> },
1017
) => {
1118
const { publisher } = await params;
12-
const clusterName = request.nextUrl.searchParams.get("cluster");
13-
14-
const cluster =
15-
clusterName === null ? Cluster.Pythnet : parseCluster(clusterName);
19+
const searchParams = request.nextUrl.searchParams;
20+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
1621

17-
if (cluster === undefined) {
18-
return NextResponse.json({ error: "Invalid Cluster" }, { status: 400 });
22+
if (!parsedSearchParams) {
23+
return new Response("Invalid params", {
24+
status: 400,
25+
});
1926
}
2027

28+
const { cluster } = parsedSearchParams;
29+
2130
if (!publisher) {
22-
return NextResponse.json(
23-
{ error: "Publisher is required" },
24-
{ status: 400 },
25-
);
31+
return new Response("Publisher is required", {
32+
status: 400,
33+
});
2634
}
2735

2836
const feeds = await getFeeds(cluster);
@@ -37,3 +45,10 @@ export const GET = async (
3745
},
3846
});
3947
};
48+
49+
const queryParamsSchema = z.object({
50+
cluster: z
51+
.enum(CLUSTER_NAMES)
52+
.transform((value) => toCluster(value))
53+
.default(ClusterToName[Cluster.Pythnet]),
54+
});
Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
1-
import { NextResponse } from "next/server";
1+
import { NextRequest } from "next/server";
22
import { stringify } from "superjson";
3+
import { z } from "zod";
4+
import { parseSearchParams } from "zod-search-params";
35

4-
import { Cluster } from "../../../../../services/pyth";
6+
import {
7+
Cluster,
8+
CLUSTER_NAMES,
9+
ClusterToName,
10+
toCluster,
11+
} from "../../../../../services/pyth";
512
import { getFeeds } from "../../../../../services/pyth/get-feeds";
613

714
export const GET = async (
8-
request: Request,
15+
request: NextRequest,
916
{ params }: { params: Promise<{ symbol: string }> },
1017
) => {
1118
const { symbol } = await params;
12-
const { searchParams } = new URL(request.url);
13-
const cluster = Number.parseInt(
14-
searchParams.get("cluster") ?? Cluster.Pythnet.toString(),
15-
) as Cluster;
19+
const searchParams = request.nextUrl.searchParams;
20+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
1621

17-
// check if cluster is valid
18-
if (cluster && !Object.values(Cluster).includes(cluster)) {
19-
return NextResponse.json({ error: "Invalid cluster" }, { status: 400 });
22+
if (!parsedSearchParams) {
23+
return new Response("Invalid params", {
24+
status: 400,
25+
});
2026
}
2127

28+
const { cluster } = parsedSearchParams;
29+
2230
const feeds = await getFeeds(cluster);
2331
const feed = feeds.find((feed) => feed.symbol === symbol);
2432

@@ -28,3 +36,10 @@ export const GET = async (
2836
},
2937
});
3038
};
39+
40+
const queryParamsSchema = z.object({
41+
cluster: z
42+
.enum(CLUSTER_NAMES)
43+
.transform((value) => toCluster(value))
44+
.default(ClusterToName[Cluster.Pythnet]),
45+
});
Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1-
import { NextResponse } from "next/server";
1+
import { NextRequest } from "next/server";
22
import { stringify } from "superjson";
3+
import { z } from "zod";
4+
import { parseSearchParams } from "zod-search-params";
35

4-
import { Cluster } from "../../../../services/pyth";
6+
import { CLUSTER_NAMES, toCluster } from "../../../../services/pyth";
57
import { getFeeds } from "../../../../services/pyth/get-feeds";
68

7-
export const GET = async (request: Request) => {
9+
export const GET = async (request: NextRequest) => {
810
// get cluster from query params
9-
const { searchParams } = new URL(request.url);
10-
const excludePriceComponents =
11-
searchParams.get("excludePriceComponents") === "true";
12-
const cluster = Number.parseInt(
13-
searchParams.get("cluster") ?? Cluster.Pythnet.toString(),
14-
) as Cluster;
11+
const searchParams = request.nextUrl.searchParams;
12+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
1513

16-
// check if cluster is valid
17-
if (cluster && !Object.values(Cluster).includes(cluster)) {
18-
return NextResponse.json({ error: "Invalid cluster" }, { status: 400 });
14+
if (!parsedSearchParams) {
15+
return new Response("Invalid params", {
16+
status: 400,
17+
});
1918
}
2019

20+
const { excludePriceComponents, cluster } = parsedSearchParams;
21+
2122
const feeds = await getFeeds(cluster);
2223
const filteredFeeds = excludePriceComponents
2324
? feeds.map((feed) => {
@@ -33,3 +34,8 @@ export const GET = async (request: Request) => {
3334
},
3435
});
3536
};
37+
38+
const queryParamsSchema = z.object({
39+
cluster: z.enum(CLUSTER_NAMES).transform((value) => toCluster(value)),
40+
excludePriceComponents: z.boolean(),
41+
});
Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,45 @@
1-
import { NextResponse } from "next/server";
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { z } from "zod";
3+
import { parseSearchParams } from "zod-search-params";
24

3-
import { Cluster } from "../../../../../services/pyth";
5+
import {
6+
Cluster,
7+
CLUSTER_NAMES,
8+
ClusterToName,
9+
toCluster,
10+
} from "../../../../../services/pyth";
411
import { getPublishersForCluster } from "../../../../../services/pyth/get-publishers-for-cluster";
512

613
export const GET = async (
7-
request: Request,
14+
request: NextRequest,
815
{ params }: { params: Promise<{ symbol: string }> },
916
) => {
1017
const { symbol } = await params;
11-
// get cluster from query params
12-
const { searchParams } = new URL(request.url);
13-
const cluster = Number.parseInt(
14-
searchParams.get("cluster") ?? Cluster.Pythnet.toString(),
15-
) as Cluster;
16-
17-
// check if cluster is valid
18-
if (cluster && !Object.values(Cluster).includes(cluster)) {
19-
return NextResponse.json({ error: "Invalid cluster" }, { status: 400 });
18+
const searchParams = request.nextUrl.searchParams;
19+
const parsedSearchParams = parseSearchParams(queryParamsSchema, searchParams);
20+
21+
if (!parsedSearchParams) {
22+
return new Response("Invalid params", {
23+
status: 400,
24+
});
2025
}
2126

27+
const { cluster } = parsedSearchParams;
28+
2229
if (!symbol) {
23-
return NextResponse.json({ error: "Symbol is required" }, { status: 400 });
30+
return new Response("Symbol is required", {
31+
status: 400,
32+
});
2433
}
2534

2635
const map = await getPublishersForCluster(cluster);
2736

2837
return NextResponse.json(map[symbol] ?? []);
2938
};
39+
40+
const queryParamsSchema = z.object({
41+
cluster: z
42+
.enum(CLUSTER_NAMES)
43+
.transform((value) => toCluster(value))
44+
.default(ClusterToName[Cluster.Pythnet]),
45+
});

apps/insights/src/server/pyth.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { parse } from "superjson";
22
import { z } from "zod";
33

44
import { PUBLIC_URL, VERCEL_AUTOMATION_BYPASS_SECRET } from "../config/server";
5-
import { Cluster, priceFeedsSchema } from "../services/pyth";
5+
import { Cluster, ClusterToName, priceFeedsSchema } from "../services/pyth";
66
import { DEFAULT_CACHE_TTL } from "../utils/cache";
77

88
export async function getPublishersForFeedRequest(
99
cluster: Cluster,
1010
symbol: string,
1111
) {
1212
const data = await fetch(
13-
`${PUBLIC_URL}/api/pyth/get-publishers/${encodeURIComponent(symbol)}?cluster=${cluster.toString()}`,
13+
`${PUBLIC_URL}/api/pyth/get-publishers/${encodeURIComponent(symbol)}?cluster=${ClusterToName[cluster]}`,
1414
{
1515
next: {
1616
revalidate: DEFAULT_CACHE_TTL,
@@ -30,7 +30,7 @@ export async function getFeedsForPublisherRequest(
3030
publisher: string,
3131
) {
3232
const data = await fetch(
33-
`${PUBLIC_URL}/api/pyth/get-feeds-for-publisher/${encodeURIComponent(publisher)}?cluster=${cluster.toString()}`,
33+
`${PUBLIC_URL}/api/pyth/get-feeds-for-publisher/${encodeURIComponent(publisher)}?cluster=${ClusterToName[cluster]}`,
3434
{
3535
next: {
3636
revalidate: DEFAULT_CACHE_TTL,
@@ -48,7 +48,7 @@ export async function getFeedsForPublisherRequest(
4848

4949
export const getFeedsRequest = async (cluster: Cluster) => {
5050
const data = await fetch(
51-
`${PUBLIC_URL}/api/pyth/get-feeds?cluster=${cluster.toString()}&excludePriceComponents=true`,
51+
`${PUBLIC_URL}/api/pyth/get-feeds?cluster=${ClusterToName[cluster]}&excludePriceComponents=true`,
5252
{
5353
next: {
5454
revalidate: DEFAULT_CACHE_TTL,
@@ -76,7 +76,7 @@ export const getFeedForSymbolRequest = async ({
7676
cluster?: Cluster;
7777
}): Promise<z.infer<typeof priceFeedsSchema>[0] | undefined> => {
7878
const data = await fetch(
79-
`${PUBLIC_URL}/api/pyth/get-feeds/${encodeURIComponent(symbol)}?cluster=${cluster.toString()}`,
79+
`${PUBLIC_URL}/api/pyth/get-feeds/${encodeURIComponent(symbol)}?cluster=${ClusterToName[cluster]}`,
8080
{
8181
next: {
8282
revalidate: DEFAULT_CACHE_TTL,

0 commit comments

Comments
 (0)