feat(deploy): #65 NestJS serverless handler entrypoint#71
Conversation
Root vercel.json was a Vite SPA rewrite (\`/(.*) → /index.html\`) that would break Next.js deploys by routing every path into a nonexistent index.html. The Next App Router handles routing itself — no rewrite needed at the Vercel layer. Per the Vercel monorepo guide and the roadmap, web and api will each become their own Vercel project with Root Directory apps/web and apps/api respectively. Root-level vercel.json would apply to both and cannot express per-app config coherently, so it stays removed. - nx affected -t lint test build --exclude web-e2e green - nx.json sharedGlobals previously referenced .circleci and .github/workflows/ci.yml, not vercel.json — no cache invalidation to worry about Refs #62
Closes the server-only half of #3 by routing every public-data-portal call through a Next Route Handler that reads the key from process.env.DATA_GO_KR_SERVICE_KEY (no NEXT_PUBLIC_ prefix). The browser bundle no longer carries the key. apps/web/app/api/data-go-kr/[...path]/route.ts (new): - Catch-all GET handler that mirrors the upstream http://apis.data.go.kr/1543061/abandonmentPublicSrvc/<path> structure - Reads search params from the incoming request, injects serviceKey and _type=json server-side, then forwards via fetch - Passes through status/content-type, returns 500 if the env var is missing, 502 if the upstream fetch throws apps/web/src/new-api/service.ts: - baseURL switched to '/api/data-go-kr' (relative path, no host) - Drops the serviceKey query param entirely — the proxy handles it - Drops the import.meta / process.env env-check code - Same export surface (getAPI, serviceAPI) so all 5 callers under new-api/{animalInfo,kind,sigungu,shelter,sido} keep working unchanged apps/web/.env.example: - NEXT_PUBLIC_DATA_GO_KR_SERVICE_KEY → DATA_GO_KR_SERVICE_KEY - Comment updated: server-only, read by the Route Handler Build output: ○ / ○ /_not-found ƒ /api/data-go-kr/[...path] ← new dynamic route ○ /like ○ /search - nx affected -t lint test build --exclude web-e2e green Refs #63
The public-data-portal endpoint moved to v2 with a renamed base path and per-resource _v2 suffixes. Update the proxy and the consumers in the same PR so the Route Handler delivered in the previous commit keeps working end-to-end. Route Handler upstream URL: - http://apis.data.go.kr/1543061/abandonmentPublicSrvc + https://apis.data.go.kr/1543061/abandonmentPublicService_v2 (now also HTTPS, matching the v2 spec at data.go.kr/data/15098931) new-api path constants (all five callers): - /abandonmentPublic → /abandonmentPublic_v2 - /sido → /sido_v2 - /sigungu → /sigungu_v2 - /kind → /kind_v2 - /shelter → /shelter_v2 shared-types AnimalInfo: - popfile (single Image URL) → popfile1 (required) plus optional popfile2..popfile8. The v2 schema returns up to 8 image URLs per rescue notice; we only render the first today, but the optional fields are typed for the gallery work in #18 e2e / future ports. apps/web AnimalCard.tsx: - item.popfile → item.popfile1 - nx affected -t lint test build --exclude web-e2e green Refs #63
@vercel/remote-nx wires Nx Remote Cache into a Vercel-hosted endpoint via tasksRunnerOptions in nx.json. Our nx.json has never had any tasksRunnerOptions wired to it (we now use the standard Nx Cloud runner via nxCloudAccessToken), so the dependency was dead weight. - grep '@vercel/remote-nx' apps libs → zero source references - nx affected -t lint test build --exclude web-e2e green (10/10 cache) Refs #64
Adds apps/api/api/index.ts as the Vercel Node Function entrypoint for the NestJS app. Local dev keeps using apps/api/src/main.ts via `nx serve api`; this handler is only consumed by Vercel. Pattern (per NestJS FAQ /faq/serverless.md): - Wrap NestFactory.create with ExpressAdapter so we can hand the underlying express app to @codegenie/serverless-express - Cache the resulting handler in a module-scope var so cold start pays the bootstrap cost once and warm invocations skip it - Export default Handler from aws-lambda types so Vercel's Node runtime auto-detects the entry Why @codegenie/ instead of @vendia/ as the roadmap mentions: @vendia/serverless-express was renamed to @codegenie/serverless- express; the Nest docs already use the new name. apps/api/tsconfig.app.json: - include adds api/**/*.ts so the new entrypoint type-checks under nx lint api / nx build api src/main.ts is untouched. webpack.config.js still bundles main.ts via @nx/webpack/app-plugin, so `nx build api` and `nx serve api` keep working identically. - nx affected -t lint test build --exclude web-e2e green - new file lints clean The Vercel project + vercel.json wiring lands in #66. Refs #65
Reviewer's GuideIntroduces a serverless-friendly NestJS entrypoint for the API app to run on Vercel’s Node runtime, while routing the web app’s public-data-portal traffic through a Next.js route handler that injects a server-only API key and migrates consumers to the v2 upstream API and response shape. Sequence diagram for browser calls through Next route handler to public data API v2sequenceDiagram
actor User
participant Browser
participant NextRouteHandler as Next_route_handler_app_api_data_go_kr
participant PublicAPI as Public_data_portal_v2
User->>Browser: Perform search in new site
Browser->>NextRouteHandler: GET /api/data-go-kr/abandonmentPublic_v2?params
NextRouteHandler->>NextRouteHandler: Read DATA_GO_KR_SERVICE_KEY from env
NextRouteHandler->>PublicAPI: GET /abandonmentPublicService_v2/abandonmentPublic_v2
Note over NextRouteHandler,PublicAPI: Adds serviceKey and _type=json query params
PublicAPI-->>NextRouteHandler: JSON response body
NextRouteHandler-->>Browser: JSON response proxy
Browser-->>User: Render results and animal images using popfile1
Sequence diagram for Vercel Node runtime invoking NestJS serverless handlersequenceDiagram
participant VercelNode as Vercel_Node_runtime
participant NestHandler as Nest_serverless_handler_index_ts
participant NestApp as Nest_application
VercelNode->>NestHandler: Invoke default Handler(event, context)
alt First invocation (cold start)
NestHandler->>NestHandler: cachedHandler is undefined
NestHandler->>NestApp: bootstrap()
NestApp->>NestApp: NestFactory.create(AppModule, ExpressAdapter)
NestApp->>NestApp: app.setGlobalPrefix(api)
NestApp->>NestApp: app.init()
NestApp-->>NestHandler: express app instance
NestHandler->>NestHandler: Wrap with serverlessExpress
NestHandler->>NestHandler: Cache resulting Handler
NestHandler-->>VercelNode: Invoke cachedHandler(event, context)
else Subsequent invocations (warm)
NestHandler->>NestHandler: Use cachedHandler
NestHandler-->>VercelNode: Invoke cachedHandler(event, context)
end
Class diagram for updated APIResponse image fields and AnimalCard usageclassDiagram
class APIResponseItem {
+string desertionNo
+string filename
+string happenDt
+string happenPlace
+string kindCd
+string colorCd
+string age
+string weight
+string noticeNo
+string noticeSdt
+string noticeEdt
+string popfile1
+string popfile2
+string popfile3
+string popfile4
+string popfile5
+string popfile6
+string popfile7
+string popfile8
+string processState
+string sexCd
+string neuterYn
}
class APIResponse~T~ {
+T responseBody
}
class AnimalCardProps {
+APIResponseItem item
}
class AnimalCard {
+AnimalCardProps props
+render()
}
AnimalCardProps o-- APIResponseItem
AnimalCard o-- AnimalCardProps
note for APIResponseItem "popfile replaced by popfile1..popfile8 for v2 image fields"
note for AnimalCard "Uses item.popfile1 as image source"
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
View your CI Pipeline Execution ↗ for commit 777191f
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In the
apps/web/app/api/data-go-kr/[...path]/route.tshandler,context.paramsis declared as aPromise, but Next App Router passes this synchronously; you can drop thePromiseandawaitto match the actual runtime shape and avoid unnecessary async wrapping. - The new Nest serverless entrypoint (
apps/api/api/index.ts) bootstraps the app with onlysetGlobalPrefix('api'); consider mirroring any additional configuration insrc/main.ts(e.g., CORS, global pipes/filters/interceptors, versioning) so behavior is consistent between localnx serveand Vercel.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In the `apps/web/app/api/data-go-kr/[...path]/route.ts` handler, `context.params` is declared as a `Promise`, but Next App Router passes this synchronously; you can drop the `Promise` and `await` to match the actual runtime shape and avoid unnecessary async wrapping.
- The new Nest serverless entrypoint (`apps/api/api/index.ts`) bootstraps the app with only `setGlobalPrefix('api')`; consider mirroring any additional configuration in `src/main.ts` (e.g., CORS, global pipes/filters/interceptors, versioning) so behavior is consistent between local `nx serve` and Vercel.
## Individual Comments
### Comment 1
<location path="apps/web/app/api/data-go-kr/[...path]/route.ts" line_range="6-8" />
<code_context>
+const UPSTREAM_BASE =
+ 'https://apis.data.go.kr/1543061/abandonmentPublicService_v2';
+
+export async function GET(
+ request: NextRequest,
+ context: { params: Promise<{ path: string[] }> }
+) {
+ const serviceKey = process.env.DATA_GO_KR_SERVICE_KEY;
</code_context>
<issue_to_address>
**issue:** The `context.params` type should not be a `Promise`; using `Promise<{ path: string[] }>` is misleading and unnecessary.
In Next.js route handlers, `context.params` is a plain object, not a promise. Typing it as `Promise<{ path: string[] }>` diverges from the framework’s types, forces unnecessary `await`s, and may break when Next’s typings change.
Use a synchronous params type instead:
```ts
export async function GET(
request: NextRequest,
context: { params: { path: string[] } }
) {
const { path } = context.params;
// ...
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| export async function GET( | ||
| request: NextRequest, | ||
| context: { params: Promise<{ path: string[] }> } |
There was a problem hiding this comment.
issue: The context.params type should not be a Promise; using Promise<{ path: string[] }> is misleading and unnecessary.
In Next.js route handlers, context.params is a plain object, not a promise. Typing it as Promise<{ path: string[] }> diverges from the framework’s types, forces unnecessary awaits, and may break when Next’s typings change.
Use a synchronous params type instead:
export async function GET(
request: NextRequest,
context: { params: { path: string[] } }
) {
const { path } = context.params;
// ...
}
Summary
Stacked on #70. Adds
apps/api/api/index.tsas the Vercel Node Function entrypoint for the NestJS app. Local dev still usesapps/api/src/main.tsvianx serve api; this handler is only consumed by Vercel.Pattern (per NestJS FAQ
/faq/serverless.md)NestFactory.createwithExpressAdapterso we can hand the underlying express app to@codegenie/serverless-expressexport default Handlerfromaws-lambdatypes so Vercel's Node runtime auto-detects the entryWhy
@codegenie/instead of@vendia/(roadmap)@vendia/serverless-expresswas renamed to@codegenie/serverless-express. The Nest docs already use the new name.Other changes
apps/api/tsconfig.app.jsonincludeaddsapi/**/*.tsso the new entrypoint type-checks undernx lint api/nx build apisrc/main.tsis untouchedwebpack.config.jsstill bundlesmain.tsvia@nx/webpack/app-plugin, sonx build apiandnx serve apikeep working identicallyThe Vercel project + per-app
vercel.jsonwiring lands in #66.Closes #65.
Test plan
npx nx affected -t lint test build --exclude web-e2egreenapi/index.tslints clean🤖 Generated with Claude Code
Summary by Sourcery
Introduce a serverless NestJS handler entrypoint for Vercel and migrate the web app to use a server-side proxy and v2 endpoints for the public data API.
New Features:
Enhancements:
Build: