Skip to content

Commit 4318b47

Browse files
committed
progress
1 parent 9b81f4b commit 4318b47

File tree

12 files changed

+236
-198
lines changed

12 files changed

+236
-198
lines changed

demo/express-typed-demo/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
"@types/express": "^4.17.21",
1010
"@types/morgan": "^1.9.9",
1111
"@types/node": "^20.12.7",
12+
"axios": "^1.6.8",
1213
"express": "^4.19.2",
13-
"morgan": "^1.10.0",
14-
"express-typed": "workspace:*"
14+
"express-typed": "workspace:*",
15+
"morgan": "^1.10.0"
1516
},
1617
"devDependencies": {
1718
"tsx": "^4.7.2"
1819
}
19-
}
20-
20+
}

demo/express-typed-demo/src/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {TypedRouter} from "express-typed"
22
import express, { Request, Response } from "express";
33
import logger from "morgan";
4-
import typedRouter from "./router";
4+
import typedRouter from "./routes/index.routes";
55

66
// Create Express server
77
export const app = express();

demo/express-typed-demo/src/nested-router/route.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

demo/express-typed-demo/src/router.ts

Lines changed: 0 additions & 54 deletions
This file was deleted.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { GetRouteResponseInfo, TypedRouter, ParseRoutes, GetRouteResponseInfoHelper, HandlerMethods, KeysWithMethod } from "express-typed";
2+
3+
import nestedRouter from "./nested.routes";
4+
5+
const typedRouter = new TypedRouter({
6+
// example usage
7+
"/": {
8+
get: (req, res) => {
9+
return res.send("Hello world").status(200);
10+
},
11+
},
12+
"/mutate": {
13+
post: (req, res) => {
14+
return res.send("Mutated!").status(201);
15+
},
16+
},
17+
"/json": {
18+
get: (req, res) => {
19+
return res.json({ message: "json" }).status(200);
20+
},
21+
},
22+
"/multiple-methods": {
23+
get: (req, res) => {
24+
return res.send("get response").status(200);
25+
},
26+
post: (req, res) => {
27+
return res.send("post response").status(201);
28+
},
29+
},
30+
// nested routers also supported
31+
"/nested": nestedRouter,
32+
});
33+
34+
export default typedRouter;
35+
36+
export type AppRoutes = ParseRoutes<typeof typedRouter>;
37+
// ^?
38+
39+
//// RouteResolver
40+
export type RouteResolver<
41+
Path extends keyof AppRoutes,
42+
Method extends keyof AppRoutes[Path],
43+
Info extends keyof GetRouteResponseInfoHelper<AppRoutes, Path, Method> | "body" = "body"
44+
> = GetRouteResponseInfo<AppRoutes, Path, Method, Info>;
45+
46+
// example usage
47+
// get the response from the home page
48+
type HomePageResponse = RouteResolver<"/", "get">;
49+
// ^?
50+
// get specific info from the response (here, the status code)
51+
type HomePageStatus = RouteResolver<"/", "get", "status">;
52+
// ^?
53+
////
54+
55+
//// RoutesWithMethod
56+
export type RoutesWithMethod<Method extends HandlerMethods> = {
57+
[key in KeysWithMethod<AppRoutes, Method>]: Method extends keyof AppRoutes[key] ? GetRouteResponseInfo<AppRoutes, key, Method> : never;
58+
};
59+
60+
// usage
61+
// get all routes that have a "get" method, and their response types
62+
type GetRoutes = RoutesWithMethod<"get">;
63+
// ^?
64+
// get all routes that have a "post" method, and their response types
65+
type PostRoutes = RoutesWithMethod<"post">;
66+
// ^?
67+
////

demo/express-typed-demo/src/nested-router/route.nested.ts renamed to demo/express-typed-demo/src/routes/nested.routes.ts

File renamed without changes.

demo/frontend-demo/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
},
1212
"dependencies": {
1313
"@tanstack/react-query": "^5.29.2",
14+
"axios": "^1.6.8",
15+
"express-typed-demo": "workspace:*",
1416
"react": "^18.2.0",
15-
"react-dom": "^18.2.0",
16-
"express-typed-demo": "workspace:*"
17+
"react-dom": "^18.2.0"
1718
},
1819
"devDependencies": {
1920
"@types/react": "^18.2.66",

demo/frontend-demo/src/App.tsx

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,23 @@
1-
import { useState } from "react";
2-
import reactLogo from "./assets/react.svg";
3-
import viteLogo from "/vite.svg";
41
import "./App.css";
5-
import { useBackendQuery } from "./useBackendQuery.ts";
2+
import { useAppQuery } from "./queries.ts";
63

7-
const Comp = () => {
8-
const query = useBackendQuery("/json");
4+
const ReactQueryApp = () => {
5+
const query = useAppQuery("/mutate", "post");
96
const data = query.data;
7+
// ^?
108
console.log("data", data);
11-
return <div>{data}</div>;
9+
10+
return (
11+
<>
12+
<div>response: {JSON.stringify(data)}</div>;
13+
</>
14+
);
1215
};
1316

1417
function App() {
15-
const [count, setCount] = useState(0);
16-
1718
return (
1819
<>
19-
<div>
20-
<Comp />
21-
<a href="https://vitejs.dev" target="_blank">
22-
<img src={viteLogo} className="logo" alt="Vite logo" />
23-
</a>
24-
<a href="https://react.dev" target="_blank">
25-
<img src={reactLogo} className="logo react" alt="React logo" />
26-
</a>
27-
</div>
28-
<h1>Vite + React</h1>
29-
<div className="card">
30-
<button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
31-
<p>
32-
Edit <code>src/App.tsx</code> and save to test HMR
33-
</p>
34-
</div>
35-
<p className="read-the-docs">Click on the Vite and React logos to learn more</p>
20+
<ReactQueryApp />
3621
</>
3722
);
3823
}

demo/frontend-demo/src/queries.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import axios, { type AxiosStatic } from "axios";
3+
import type { AppRoutes, RouteResolver, RoutesWithMethod } from "express-typed-demo/src/routes/index.routes";
4+
5+
// an hook to fetch response from server, for any possible method(GET, POST, PUT, DELETE)
6+
export const useAppQuery = <Path extends keyof AppRoutes, Method extends Extract<keyof AxiosStatic, keyof AppRoutes[Path]>>(
7+
path: Path,
8+
method: Method
9+
) => {
10+
return useQuery<RouteResolver<Path, Method>>({
11+
queryKey: [path],
12+
queryFn: async () => {
13+
const res = await (axios as any)[method](`/api${path}`);
14+
return res.data as RouteResolver<Path, Method>;
15+
},
16+
});
17+
};
18+
19+
// an hook to fetch response from server, for GET method
20+
type GetRoutes = RoutesWithMethod<"get">;
21+
export const useAppGetQuery = <P extends keyof GetRoutes>(path: P) => {
22+
return useQuery<GetRoutes[P]>({
23+
queryKey: [path],
24+
queryFn: async () => {
25+
const res = await axios.get(`/api${path}`);
26+
return res.data as GetRoutes[P];
27+
},
28+
});
29+
};
30+
31+
// an hook to fetch response from server, for POST method
32+
type PostRoutes = RoutesWithMethod<"post">;
33+
export const useAppPostQuery = <P extends keyof PostRoutes>(path: P) => {
34+
return useQuery<PostRoutes[P]>({
35+
queryKey: [path],
36+
queryFn: async () => {
37+
const res = await axios.post(`/api${path}`);
38+
return res.data as PostRoutes[P];
39+
},
40+
});
41+
};

demo/frontend-demo/src/useBackendQuery.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)