How close can explicit HTTP endpoints get to server-function DX?
Next.js, SolidStart, TanStack Start, and SvelteKit all offer some form of server or remote functions. The integrations differ, but being able to connect server code to forms and data fetching is appealing. I've used Nuxt since its early releases, and I like Nuxt 5's work on typed HTTP routes. Seeing those approaches develop made me wonder: How much of that DX can we get while keeping an explicit…
Next.js, SolidStart, TanStack Start, and SvelteKit all provide some form of server or remote functions. Being able to connect server code to forms and data fetching is appealing. The author has used Nuxt since its early releases and appreciates Nuxt 5's work on typed HTTP routes. This has led to curiosity about how much of that developer experience (DX) can be achieved while keeping an explicit HTTP endpoint as the starting point. The author is exploring this concept in Nuxt Endpoints.
Routes in Nuxt Endpoints retain their files, URLs, and HTTP methods. The defineRouteHandler() function builds upon the validated-routing design being discussed in H3 RFC #1437, incorporating request and response schemas alongside the handler. This contract enables other tools to understand the expectations. Nuxt Endpoints expands upon this design to integrate with client behavior, including pagination and forms.
The primary idea is that a server contract should do more than only type the response. It should also constrain the handler and allow the client to utilize features dependent on that contract. An example of this is cursor pagination, where a route is defined for GET /api/articles. The database query and cursor encoding are handled by the author's code.
Nuxt Endpoints takes care of the pagination contract, including validated cursor and limit query fields, the 200 response format, and ensuring the handler's return matches the defined shape. If the response doesn't match the contract, a server-side type error is raised. The generated OpenAPI document exposes these fields, and the client can use an adapter that requires this contract. Returning data without the required properties results in a server-side type error.
The route's format is Nuxt Endpoints' convention, not an HTTP standard. The contract verifies the response shape but cannot guarantee the correctness of the database query. The client can pass the same typed request to Pinia Colada, which handles pagination, reactive state, and refetching lifecycle. The client can use the typed request with Pinia Colada's infiniteQueryOptions function, which rejects endpoints lacking the cursor-pagination contract at compile time.
For handling different HTTP status codes, the author demonstrates a detail route with 200 and 404 responses. By checking the status, the body type can be narrowed accordingly. This approach allows handling specific status codes, such as 404, without encountering thrown fetch errors. Network failures still reject the request.
These types describe the declared responses and cannot account for every possible response from a proxy or other infrastructure. The same route can be called from another service or using curl. Its HTTP interface is documented in the generated OpenAPI, so callers don't need Nuxt Endpoints to use it. The experimental Nuxt 5 prototype extends this idea to forms, enabling progressively enhanced forms with a server-side POST route.
The server and form validation are defined similarly to the API route example. The server and client can handle the response based on the status code, providing a more structured and predictable development experience.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.