History

move API/MCP dispatch to Start request middleware

Rhys Sullivan committed 7 months ago

Commit 4b944b51 parent 236b76e9 files · +41 −118Browse files

Unified · Split

9 files
  1. apps/cloud/src/routeTree.gen.ts +2 −22
  2. apps/cloud/src/routes/api.$.ts +0 −18
  3. apps/cloud/src/server/api-handler.ts +2 −2
  4. apps/cloud/src/start.ts +15 −0
  5. apps/local/src/routeTree.gen.ts +2 −43
  6. apps/local/src/routes/api.$.ts +0 −18
  7. apps/local/src/routes/mcp.$.ts +0 −13
  8. apps/local/src/server/api-handler.ts +2 −2
  9. apps/local/src/start.ts +18 −0
apps/cloud/src/routeTree.gen.ts+2−22
@@ -13,7 +13,6 @@ import { Route as ToolsRouteImport } from './routes/tools' · 12 unmodified lines
13import { Route as SecretsRouteImport } from './routes/secrets'13import { Route as SecretsRouteImport } from './routes/secrets'
14import { Route as IndexRouteImport } from './routes/index'14import { Route as IndexRouteImport } from './routes/index'
15import { Route as SourcesNamespaceRouteImport } from './routes/sources.$namespace'15import { Route as SourcesNamespaceRouteImport } from './routes/sources.$namespace'
16import { Route as ApiSplatRouteImport } from './routes/api.$'
17import { Route as SourcesAddPluginKeyRouteImport } from './routes/sources.add.$pluginKey'16import { Route as SourcesAddPluginKeyRouteImport } from './routes/sources.add.$pluginKey'
18 17
19const ToolsRoute = ToolsRouteImport.update({18const ToolsRoute = ToolsRouteImport.update({
@@ -36,11 +35,6 @@ const SourcesNamespaceRoute = SourcesNamespaceRouteImport.update({ · 16 unmodified lines
36 path: '/sources/$namespace',35 path: '/sources/$namespace',
37 getParentRoute: () => rootRouteImport,36 getParentRoute: () => rootRouteImport,
38} as any)37} as any)
39const ApiSplatRoute = ApiSplatRouteImport.update({
40 id: '/api/$',
41 path: '/api/$',
42 getParentRoute: () => rootRouteImport,
43} as any)
44const SourcesAddPluginKeyRoute = SourcesAddPluginKeyRouteImport.update({38const SourcesAddPluginKeyRoute = SourcesAddPluginKeyRouteImport.update({
45 id: '/sources/add/$pluginKey',39 id: '/sources/add/$pluginKey',
46 path: '/sources/add/$pluginKey',40 path: '/sources/add/$pluginKey',
@@ -51,7 +45,6 @@ export interface FileRoutesByFullPath { · 4 unmodified lines
51 '/': typeof IndexRoute45 '/': typeof IndexRoute
52 '/secrets': typeof SecretsRoute46 '/secrets': typeof SecretsRoute
53 '/tools': typeof ToolsRoute47 '/tools': typeof ToolsRoute
54 '/api/$': typeof ApiSplatRoute
55 '/sources/$namespace': typeof SourcesNamespaceRoute48 '/sources/$namespace': typeof SourcesNamespaceRoute
56 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute49 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute
57}50}
@@ -59,7 +52,6 @@ export interface FileRoutesByTo { · 1 unmodified line
59 '/': typeof IndexRoute52 '/': typeof IndexRoute
60 '/secrets': typeof SecretsRoute53 '/secrets': typeof SecretsRoute
61 '/tools': typeof ToolsRoute54 '/tools': typeof ToolsRoute
62 '/api/$': typeof ApiSplatRoute
63 '/sources/$namespace': typeof SourcesNamespaceRoute55 '/sources/$namespace': typeof SourcesNamespaceRoute
64 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute56 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute
65}57}
@@ -68,7 +60,6 @@ export interface FileRoutesById { · 2 unmodified lines
68 '/': typeof IndexRoute60 '/': typeof IndexRoute
69 '/secrets': typeof SecretsRoute61 '/secrets': typeof SecretsRoute
70 '/tools': typeof ToolsRoute62 '/tools': typeof ToolsRoute
71 '/api/$': typeof ApiSplatRoute
72 '/sources/$namespace': typeof SourcesNamespaceRoute63 '/sources/$namespace': typeof SourcesNamespaceRoute
73 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute64 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute
74}65}
@@ -78,7 +69,6 @@ export interface FileRouteTypes { · 3 unmodified lines
78 | '/'69 | '/'
79 | '/secrets'70 | '/secrets'
80 | '/tools'71 | '/tools'
81 | '/api/$'
82 | '/sources/$namespace'72 | '/sources/$namespace'
83 | '/sources/add/$pluginKey'73 | '/sources/add/$pluginKey'
84 fileRoutesByTo: FileRoutesByTo74 fileRoutesByTo: FileRoutesByTo
@@ -86,7 +76,6 @@ export interface FileRouteTypes { · 1 unmodified line
86 | '/'76 | '/'
87 | '/secrets'77 | '/secrets'
88 | '/tools'78 | '/tools'
89 | '/api/$'
90 | '/sources/$namespace'79 | '/sources/$namespace'
91 | '/sources/add/$pluginKey'80 | '/sources/add/$pluginKey'
92 id:81 id:
@@ -94,7 +83,6 @@ export interface FileRouteTypes { · 1 unmodified line
94 | '/'83 | '/'
95 | '/secrets'84 | '/secrets'
96 | '/tools'85 | '/tools'
97 | '/api/$'
98 | '/sources/$namespace'86 | '/sources/$namespace'
99 | '/sources/add/$pluginKey'87 | '/sources/add/$pluginKey'
100 fileRoutesById: FileRoutesById88 fileRoutesById: FileRoutesById
@@ -103,7 +91,6 @@ export interface RootRouteChildren { · 2 unmodified lines
103 IndexRoute: typeof IndexRoute91 IndexRoute: typeof IndexRoute
104 SecretsRoute: typeof SecretsRoute92 SecretsRoute: typeof SecretsRoute
105 ToolsRoute: typeof ToolsRoute93 ToolsRoute: typeof ToolsRoute
106 ApiSplatRoute: typeof ApiSplatRoute
107 SourcesNamespaceRoute: typeof SourcesNamespaceRoute94 SourcesNamespaceRoute: typeof SourcesNamespaceRoute
108 SourcesAddPluginKeyRoute: typeof SourcesAddPluginKeyRoute95 SourcesAddPluginKeyRoute: typeof SourcesAddPluginKeyRoute
109}96}
@@ -138,13 +125,6 @@ declare module '@tanstack/react-router' { · 28 unmodified lines
138 preLoaderRoute: typeof SourcesNamespaceRouteImport125 preLoaderRoute: typeof SourcesNamespaceRouteImport
139 parentRoute: typeof rootRouteImport126 parentRoute: typeof rootRouteImport
140 }127 }
141 '/api/$': {
142 id: '/api/$'
143 path: '/api/$'
144 fullPath: '/api/$'
145 preLoaderRoute: typeof ApiSplatRouteImport
146 parentRoute: typeof rootRouteImport
147 }
148 '/sources/add/$pluginKey': {128 '/sources/add/$pluginKey': {
149 id: '/sources/add/$pluginKey'129 id: '/sources/add/$pluginKey'
150 path: '/sources/add/$pluginKey'130 path: '/sources/add/$pluginKey'
@@ -159,7 +139,6 @@ const rootRouteChildren: RootRouteChildren = { · 8 unmodified lines
159 IndexRoute: IndexRoute,139 IndexRoute: IndexRoute,
160 SecretsRoute: SecretsRoute,140 SecretsRoute: SecretsRoute,
161 ToolsRoute: ToolsRoute,141 ToolsRoute: ToolsRoute,
162 ApiSplatRoute: ApiSplatRoute,
163 SourcesNamespaceRoute: SourcesNamespaceRoute,142 SourcesNamespaceRoute: SourcesNamespaceRoute,
164 SourcesAddPluginKeyRoute: SourcesAddPluginKeyRoute,143 SourcesAddPluginKeyRoute: SourcesAddPluginKeyRoute,
165}144}
@@ -168,10 +147,11 @@ export const routeTree = rootRouteImport · 2 unmodified lines
168 ._addFileTypes<FileRouteTypes>()147 ._addFileTypes<FileRouteTypes>()
169 148
170import type { getRouter } from './router.tsx'149import type { getRouter } from './router.tsx'
171import type { createStart } from '@tanstack/react-start'150import type { startInstance } from './start.ts'
172declare module '@tanstack/react-start' {151declare module '@tanstack/react-start' {
173 interface Register {152 interface Register {
174 ssr: true153 ssr: true
175 router: Awaited<ReturnType<typeof getRouter>>154 router: Awaited<ReturnType<typeof getRouter>>
155 config: Awaited<ReturnType<typeof startInstance.getOptions>>
176 }156 }
177}157}
apps/cloud/src/routes/api.$.ts+0−18
@@ -1,18 +0,0 @@
1import { createFileRoute } from "@tanstack/react-router";
2
3const handle = async (request: Request) => {
4 const { handleApiRequest } = await import("../server/api-handler");
5 return handleApiRequest(request);
6};
7
8export const Route = createFileRoute("/api/$")({
9 server: {
10 handlers: {
11 GET: ({ request }) => handle(request),
12 POST: ({ request }) => handle(request),
13 PUT: ({ request }) => handle(request),
14 DELETE: ({ request }) => handle(request),
15 PATCH: ({ request }) => handle(request),
16 },
17 },
18});
apps/cloud/src/server/api-handler.ts+2−2
@@ -1,8 +1,8 @@
1import { handleApiRequest as _handleApiRequest } from "../api";1import { handleApiRequest as _handleApiRequest } from "../api";
2 2
3export const handleApiRequest = (request: Request) => {3export const handleApiRequest = (request: Request) => {
4 // Strip /api prefix — route catch-all (api.$.ts) matches /api/*4 // Strip /api prefix — Start request middleware forwards /api/* here,
5 // but Effect endpoints are defined without the prefix5 // but Effect endpoints are defined without the prefix.
6 const url = new URL(request.url);6 const url = new URL(request.url);
7 url.pathname = url.pathname.replace(/^\/api/, "");7 url.pathname = url.pathname.replace(/^\/api/, "");
8 return _handleApiRequest(new Request(url, request));8 return _handleApiRequest(new Request(url, request));
apps/cloud/src/start.ts+15−0
@@ -0,0 +1,15 @@
1import { createMiddleware, createStart } from "@tanstack/react-start";
2import { handleApiRequest } from "./server/api-handler";
3
4const apiRequestMiddleware = createMiddleware({ type: "request" }).server(
5 ({ pathname, request, next }) => {
6 if (pathname === "/api" || pathname.startsWith("/api/")) {
7 return handleApiRequest(request);
8 }
9 return next();
10 },
11);
12
13export const startInstance = createStart(() => ({
14 requestMiddleware: [apiRequestMiddleware],
15}));
apps/local/src/routeTree.gen.ts+2−43
@@ -13,8 +13,6 @@ import { Route as ToolsRouteImport } from './routes/tools' · 12 unmodified lines
13import { Route as SecretsRouteImport } from './routes/secrets'13import { Route as SecretsRouteImport } from './routes/secrets'
14import { Route as IndexRouteImport } from './routes/index'14import { Route as IndexRouteImport } from './routes/index'
15import { Route as SourcesNamespaceRouteImport } from './routes/sources.$namespace'15import { Route as SourcesNamespaceRouteImport } from './routes/sources.$namespace'
16import { Route as McpSplatRouteImport } from './routes/mcp.$'
17import { Route as ApiSplatRouteImport } from './routes/api.$'
18import { Route as SourcesAddPluginKeyRouteImport } from './routes/sources.add.$pluginKey'16import { Route as SourcesAddPluginKeyRouteImport } from './routes/sources.add.$pluginKey'
19 17
20const ToolsRoute = ToolsRouteImport.update({18const ToolsRoute = ToolsRouteImport.update({
@@ -37,16 +35,6 @@ const SourcesNamespaceRoute = SourcesNamespaceRouteImport.update({ · 16 unmodified lines
37 path: '/sources/$namespace',35 path: '/sources/$namespace',
38 getParentRoute: () => rootRouteImport,36 getParentRoute: () => rootRouteImport,
39} as any)37} as any)
40const McpSplatRoute = McpSplatRouteImport.update({
41 id: '/mcp/$',
42 path: '/mcp/$',
43 getParentRoute: () => rootRouteImport,
44} as any)
45const ApiSplatRoute = ApiSplatRouteImport.update({
46 id: '/api/$',
47 path: '/api/$',
48 getParentRoute: () => rootRouteImport,
49} as any)
50const SourcesAddPluginKeyRoute = SourcesAddPluginKeyRouteImport.update({38const SourcesAddPluginKeyRoute = SourcesAddPluginKeyRouteImport.update({
51 id: '/sources/add/$pluginKey',39 id: '/sources/add/$pluginKey',
52 path: '/sources/add/$pluginKey',40 path: '/sources/add/$pluginKey',
@@ -57,8 +45,6 @@ export interface FileRoutesByFullPath { · 4 unmodified lines
57 '/': typeof IndexRoute45 '/': typeof IndexRoute
58 '/secrets': typeof SecretsRoute46 '/secrets': typeof SecretsRoute
59 '/tools': typeof ToolsRoute47 '/tools': typeof ToolsRoute
60 '/api/$': typeof ApiSplatRoute
61 '/mcp/$': typeof McpSplatRoute
62 '/sources/$namespace': typeof SourcesNamespaceRoute48 '/sources/$namespace': typeof SourcesNamespaceRoute
63 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute49 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute
64}50}
@@ -66,8 +52,6 @@ export interface FileRoutesByTo { · 1 unmodified line
66 '/': typeof IndexRoute52 '/': typeof IndexRoute
67 '/secrets': typeof SecretsRoute53 '/secrets': typeof SecretsRoute
68 '/tools': typeof ToolsRoute54 '/tools': typeof ToolsRoute
69 '/api/$': typeof ApiSplatRoute
70 '/mcp/$': typeof McpSplatRoute
71 '/sources/$namespace': typeof SourcesNamespaceRoute55 '/sources/$namespace': typeof SourcesNamespaceRoute
72 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute56 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute
73}57}
@@ -76,8 +60,6 @@ export interface FileRoutesById { · 2 unmodified lines
76 '/': typeof IndexRoute60 '/': typeof IndexRoute
77 '/secrets': typeof SecretsRoute61 '/secrets': typeof SecretsRoute
78 '/tools': typeof ToolsRoute62 '/tools': typeof ToolsRoute
79 '/api/$': typeof ApiSplatRoute
80 '/mcp/$': typeof McpSplatRoute
81 '/sources/$namespace': typeof SourcesNamespaceRoute63 '/sources/$namespace': typeof SourcesNamespaceRoute
82 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute64 '/sources/add/$pluginKey': typeof SourcesAddPluginKeyRoute
83}65}
@@ -87,8 +69,6 @@ export interface FileRouteTypes { · 3 unmodified lines
87 | '/'69 | '/'
88 | '/secrets'70 | '/secrets'
89 | '/tools'71 | '/tools'
90 | '/api/$'
91 | '/mcp/$'
92 | '/sources/$namespace'72 | '/sources/$namespace'
93 | '/sources/add/$pluginKey'73 | '/sources/add/$pluginKey'
94 fileRoutesByTo: FileRoutesByTo74 fileRoutesByTo: FileRoutesByTo
@@ -96,8 +76,6 @@ export interface FileRouteTypes { · 1 unmodified line
96 | '/'76 | '/'
97 | '/secrets'77 | '/secrets'
98 | '/tools'78 | '/tools'
99 | '/api/$'
100 | '/mcp/$'
101 | '/sources/$namespace'79 | '/sources/$namespace'
102 | '/sources/add/$pluginKey'80 | '/sources/add/$pluginKey'
103 id:81 id:
@@ -105,8 +83,6 @@ export interface FileRouteTypes { · 1 unmodified line
105 | '/'83 | '/'
106 | '/secrets'84 | '/secrets'
107 | '/tools'85 | '/tools'
108 | '/api/$'
109 | '/mcp/$'
110 | '/sources/$namespace'86 | '/sources/$namespace'
111 | '/sources/add/$pluginKey'87 | '/sources/add/$pluginKey'
112 fileRoutesById: FileRoutesById88 fileRoutesById: FileRoutesById
@@ -115,8 +91,6 @@ export interface RootRouteChildren { · 2 unmodified lines
115 IndexRoute: typeof IndexRoute91 IndexRoute: typeof IndexRoute
116 SecretsRoute: typeof SecretsRoute92 SecretsRoute: typeof SecretsRoute
117 ToolsRoute: typeof ToolsRoute93 ToolsRoute: typeof ToolsRoute
118 ApiSplatRoute: typeof ApiSplatRoute
119 McpSplatRoute: typeof McpSplatRoute
120 SourcesNamespaceRoute: typeof SourcesNamespaceRoute94 SourcesNamespaceRoute: typeof SourcesNamespaceRoute
121 SourcesAddPluginKeyRoute: typeof SourcesAddPluginKeyRoute95 SourcesAddPluginKeyRoute: typeof SourcesAddPluginKeyRoute
122}96}
@@ -151,20 +125,6 @@ declare module '@tanstack/react-router' { · 28 unmodified lines
151 preLoaderRoute: typeof SourcesNamespaceRouteImport125 preLoaderRoute: typeof SourcesNamespaceRouteImport
152 parentRoute: typeof rootRouteImport126 parentRoute: typeof rootRouteImport
153 }127 }
154 '/mcp/$': {
155 id: '/mcp/$'
156 path: '/mcp/$'
157 fullPath: '/mcp/$'
158 preLoaderRoute: typeof McpSplatRouteImport
159 parentRoute: typeof rootRouteImport
160 }
161 '/api/$': {
162 id: '/api/$'
163 path: '/api/$'
164 fullPath: '/api/$'
165 preLoaderRoute: typeof ApiSplatRouteImport
166 parentRoute: typeof rootRouteImport
167 }
168 '/sources/add/$pluginKey': {128 '/sources/add/$pluginKey': {
169 id: '/sources/add/$pluginKey'129 id: '/sources/add/$pluginKey'
170 path: '/sources/add/$pluginKey'130 path: '/sources/add/$pluginKey'
@@ -179,8 +139,6 @@ const rootRouteChildren: RootRouteChildren = { · 8 unmodified lines
179 IndexRoute: IndexRoute,139 IndexRoute: IndexRoute,
180 SecretsRoute: SecretsRoute,140 SecretsRoute: SecretsRoute,
181 ToolsRoute: ToolsRoute,141 ToolsRoute: ToolsRoute,
182 ApiSplatRoute: ApiSplatRoute,
183 McpSplatRoute: McpSplatRoute,
184 SourcesNamespaceRoute: SourcesNamespaceRoute,142 SourcesNamespaceRoute: SourcesNamespaceRoute,
185 SourcesAddPluginKeyRoute: SourcesAddPluginKeyRoute,143 SourcesAddPluginKeyRoute: SourcesAddPluginKeyRoute,
186}144}
@@ -189,10 +147,11 @@ export const routeTree = rootRouteImport · 2 unmodified lines
189 ._addFileTypes<FileRouteTypes>()147 ._addFileTypes<FileRouteTypes>()
190 148
191import type { getRouter } from './router.tsx'149import type { getRouter } from './router.tsx'
192import type { createStart } from '@tanstack/react-start'150import type { startInstance } from './start.ts'
193declare module '@tanstack/react-start' {151declare module '@tanstack/react-start' {
194 interface Register {152 interface Register {
195 ssr: true153 ssr: true
196 router: Awaited<ReturnType<typeof getRouter>>154 router: Awaited<ReturnType<typeof getRouter>>
155 config: Awaited<ReturnType<typeof startInstance.getOptions>>
197 }156 }
198}157}
apps/local/src/routes/api.$.ts+0−18
@@ -1,18 +0,0 @@
1import { createFileRoute } from "@tanstack/react-router";
2
3const handle = async (request: Request) => {
4 const { handleApiRequest } = await import("../server/api-handler");
5 return handleApiRequest(request);
6};
7
8export const Route = createFileRoute("/api/$")({
9 server: {
10 handlers: {
11 GET: ({ request }) => handle(request),
12 POST: ({ request }) => handle(request),
13 PUT: ({ request }) => handle(request),
14 DELETE: ({ request }) => handle(request),
15 PATCH: ({ request }) => handle(request),
16 },
17 },
18});
apps/local/src/routes/mcp.$.ts+0−13
@@ -1,13 +0,0 @@
1import { createFileRoute } from "@tanstack/react-router";
2import { handleMcpRequest } from "../server/api-handler";
3
4export const Route = createFileRoute("/mcp/$")({
5 server: {
6 handlers: {
7 GET: ({ request }) => handleMcpRequest(request),
8 POST: ({ request }) => handleMcpRequest(request),
9 PUT: ({ request }) => handleMcpRequest(request),
10 DELETE: ({ request }) => handleMcpRequest(request),
11 },
12 },
13});
apps/local/src/server/api-handler.ts+2−2
@@ -20,8 +20,8 @@ const getHandlers = () => { · 19 unmodified lines
20 20
21export const handleApiRequest = async (request: Request): Promise<Response> => {21export const handleApiRequest = async (request: Request): Promise<Response> => {
22 const handlers = await getHandlers();22 const handlers = await getHandlers();
23 // Strip /api prefix — the route catch-all (api.$.ts) matches /api/*23 // Strip /api prefix — Start request middleware forwards /api/* here,
24 // but the Effect handler endpoints are defined without the prefix24 // but the Effect handler endpoints are defined without the prefix.
25 const url = new URL(request.url);25 const url = new URL(request.url);
26 url.pathname = url.pathname.replace(/^\/api/, "");26 url.pathname = url.pathname.replace(/^\/api/, "");
27 return handlers.api.handler(new Request(url, request));27 return handlers.api.handler(new Request(url, request));
apps/local/src/start.ts+18−0
@@ -0,0 +1,18 @@
1import { createMiddleware, createStart } from "@tanstack/react-start";
2import { handleApiRequest, handleMcpRequest } from "./server/api-handler";
3
4const serverRequestMiddleware = createMiddleware({ type: "request" }).server(
5 ({ pathname, request, next }) => {
6 if (pathname === "/api" || pathname.startsWith("/api/")) {
7 return handleApiRequest(request);
8 }
9 if (pathname === "/mcp" || pathname.startsWith("/mcp/")) {
10 return handleMcpRequest(request);
11 }
12 return next();
13 },
14);
15
16export const startInstance = createStart(() => ({
17 requestMiddleware: [serverRequestMiddleware],
18}));