@rest-rpc/next 0.1.0-beta.0 → 0.1.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,44 +2,80 @@
2
2
 
3
3
  REST-shaped APIs with function-shaped TypeScript.
4
4
 
5
- `rest-rpc` lets you define HTTP routes in a shared TypeScript contract, then use
6
- that contract to derive typed server handlers, typed fetch clients, TanStack Query
7
- options, and OpenAPI documents.
5
+ Define your HTTP API once as a shared TypeScript contract, then derive typed
6
+ server handlers, fetch clients, openAPI documents, and more from it.
8
7
 
9
- The API stays REST-shaped. Everyday application code can feel like function
10
- calls.
8
+ ## Features
9
+
10
+ - Shared TypeScript contracts for HTTP APIs.
11
+ - Typed server handlers for Express, Hono, Fastify, Next.js, and Fetch runtimes.
12
+ - Typed fetch client.
13
+ - Typed TanStack Query helpers.
14
+ - Typed WebSockets, streaming, non-JSON requests/responses.
15
+ - OpenAPI documents generated from the TypeScript contract.
16
+ - Standard Schema support.
17
+
18
+ ## Minimal Example
19
+
20
+ Define a contract
11
21
 
12
22
  ```ts
13
- const todo = await api.todos.get.fetch({ id: "todo_1" });
23
+ import { router } from "@rest-rpc/core";
24
+ import { z } from "zod";
25
+
26
+ export const api = router({
27
+ todos: {
28
+ get: {
29
+ method: "GET",
30
+ path: "/todos/:id",
31
+ response: z.object({
32
+ id: z.string(),
33
+ title: z.string(),
34
+ }),
35
+ },
36
+ },
37
+ });
14
38
  ```
15
39
 
16
- That call is still a normal HTTP request:
40
+ Implement the contract on the server
17
41
 
18
- ```http
19
- GET /todos/todo_1
20
- ```
42
+ ```ts
43
+ const routes = router(api, {
44
+ todos: {
45
+ get({ id }) {
46
+ return getTodo(id);
47
+ },
48
+ },
49
+ });
21
50
 
22
- ## Packages
51
+ registerRoutes(app, routes);
52
+ ```
23
53
 
24
- - `@rest-rpc/core`: contracts, fetch client, OpenAPI generation, core helpers.
25
- - `@rest-rpc/express`: Express server adapter.
26
- - `@rest-rpc/hono`: Hono server adapter.
27
- - `@rest-rpc/fastify`: Fastify server adapter.
28
- - `@rest-rpc/web`: Web `Request`/`Response` HTTP handler adapter.
29
- - `@rest-rpc/next`: Next.js server/client adapter.
30
- - `@rest-rpc/tanstack-query`: TanStack Query options and key helpers.
54
+ use the contract on the client with RPC-style function calls
31
55
 
32
- `@rest-rpc/server` contains shared adapter infrastructure and is mainly useful
33
- for adapter authors.
56
+ ```ts
57
+ import { initClient } from "@rest-rpc/core";
58
+ import { api } from "./contract";
34
59
 
35
- ## Design
60
+ const client = initClient(api, {
61
+ baseUrl: "https://api.example.com",
62
+ });
36
63
 
37
- - REST routes remain explicit in the contract.
38
- - Handler and client calls use flattened request input.
39
- - Responses are keyed by HTTP status.
40
- - Server adapters are thin integrations with existing frameworks.
41
- - Schema libraries, OpenAPI UI, middleware, auth, and app structure stay your choice.
64
+ const todo = await client.todos.get.fetch({
65
+ id: "todo_1",
66
+ });
67
+ ```
42
68
 
43
69
  ## Documentation
44
70
 
45
- Full documentation will live at [rest-rpc.dev](https://rest-rpc.dev).
71
+ Full documentation is available at [rest-rpc.dev](https://rest-rpc.dev)
72
+
73
+ ## Packages
74
+
75
+ - [`@rest-rpc/core`](https://npmx.dev/package/@rest-rpc/core): API contract, client and openAPI generation.
76
+ - [`@rest-rpc/express`](https://npmx.dev/package/@rest-rpc/express): Express server adapter.
77
+ - [`@rest-rpc/fastify`](https://npmx.dev/package/@rest-rpc/fastify): Fastify server adapter.
78
+ - [`@rest-rpc/hono`](https://npmx.dev/package/@rest-rpc/hono): Hono server adapter.
79
+ - [`@rest-rpc/next`](https://npmx.dev/package/@rest-rpc/next): Next.js adapter.
80
+ - [`@rest-rpc/web`](https://npmx.dev/package/@rest-rpc/web): Fetch runtime adapter
81
+ - [`@rest-rpc/tanstack-query`](https://npmx.dev/package/@rest-rpc/tanstack-query): TanStack Query adapter.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,2 @@
1
- export { ContractResponseError } from "@rest-rpc/web";
2
- export type { NextClientOptions } from "./client.ts";
3
- export { getGeneratedTagsForRoute, initNextClient, } from "./client.ts";
4
- export { type CreateRouteHandlerOptions, createRouteHandler, createRouterHandler, type NextRouteHandlerContext, type NextRouteParseBody, type NextRouteParseBodyInput, type RouteHandler, type RouteRequest, type RouteResponse, } from "./server.ts";
1
+ export { type ClearCookieOptions, ContractResponseError, type CookiePriority, clearCookie, type SameSite, type SetCookieOptions, setCookie, } from "@rest-rpc/web";
2
+ export { type CreateRouteHandlerOptions, createRouteHandler, type NextRouteMiddleware, type RouteRequest, type RouteResponse, route, router, } from "./server.ts";
package/dist/index.js CHANGED
@@ -1,3 +1,2 @@
1
- export { ContractResponseError } from "@rest-rpc/web";
2
- export { getGeneratedTagsForRoute, initNextClient, } from "./client.js";
3
- export { createRouteHandler, createRouterHandler, } from "./server.js";
1
+ export { ContractResponseError, clearCookie, setCookie, } from "@rest-rpc/web";
2
+ export { createRouteHandler, route, router, } from "./server.js";
package/dist/server.d.ts CHANGED
@@ -1,30 +1,19 @@
1
- import type { HttpMethod, HttpRouteDeclaration, RequestBodySchema } from "@rest-rpc/core/contract";
2
- import { type CreateWebHandlerOptions, type RouteHandler as WebRouteHandler, type WebRouteParseBodyInput, type RouteRequest as WebRouteRequest, type RouteResponse as WebRouteResponse } from "@rest-rpc/web";
3
- type WebContract = HttpRouteDeclaration | {
4
- [key: string]: WebContract;
5
- };
6
- type WebRequestHandler = (request: Request) => Promise<Response>;
7
- type WebRouterHandlers<TContract extends WebContract> = TContract extends HttpRouteDeclaration ? RouteHandler<TContract> : {
8
- [K in keyof TContract]: TContract[K] extends WebContract ? WebRouterHandlers<TContract[K]> : never;
9
- };
10
- type RouteHandlerMap<E extends HttpRouteDeclaration> = {
11
- [K in E["method"]]: WebRequestHandler;
12
- };
13
- type RouterHandlerMap = {
14
- [K in HttpMethod]: WebRequestHandler;
15
- };
16
- export type NextRouteHandlerContext = {
17
- request: Request;
18
- };
19
- export type RouteRequest<E extends HttpRouteDeclaration> = WebRouteRequest<E, NextRouteHandlerContext>;
1
+ import type { HttpRouteDeclaration } from "@rest-rpc/core/contract";
2
+ import { type CreateWebHandlerOptions, type WebContract, type WebImplementationTree, type WebRouteBuilder, type WebRouteMiddleware, type WebRouteParseBodyInput, type RouteRequest as WebRouteRequest, type RouteResponse as WebRouteResponse, type WebRouterBuilder } from "@rest-rpc/web";
3
+ import type { NextRequest } from "next/server.js";
4
+ export type NextRouteMiddleware<TContext extends Record<string, unknown>> = WebRouteMiddleware<Record<never, never>, TContext, NextRequest>;
5
+ export type RouteRequest<E extends HttpRouteDeclaration, TContext extends Record<string, unknown> = Record<string, unknown>> = WebRouteRequest<E, TContext, NextRequest>;
20
6
  export type RouteResponse<E extends HttpRouteDeclaration> = WebRouteResponse<E>;
21
- export type RouteHandler<E extends HttpRouteDeclaration> = WebRouteHandler<E, NextRouteHandlerContext>;
22
- export type NextRouteParseBodyInput = WebRouteParseBodyInput;
23
- export type NextRouteParseBody = (input: NextRouteParseBodyInput) => unknown | Promise<unknown>;
24
7
  export type CreateRouteHandlerOptions = {
25
- errorHandlers?: CreateWebHandlerOptions<NextRouteHandlerContext>["errorHandlers"];
26
- parseBody?: NextRouteParseBody;
8
+ errorHandlers?: CreateWebHandlerOptions["errorHandlers"];
9
+ parseBody?: (input: WebRouteParseBodyInput) => unknown | Promise<unknown>;
10
+ };
11
+ export declare const route: <const TRoute extends HttpRouteDeclaration>(contract: TRoute) => WebRouteBuilder<TRoute, Record<never, never>, NextRequest>;
12
+ export declare const router: <const TContract extends WebContract>(contract: TContract) => WebRouterBuilder<TContract, Record<never, never>, NextRequest>;
13
+ export declare const createRouteHandler: (implementations: WebImplementationTree, options?: CreateRouteHandlerOptions) => {
14
+ DELETE: (request: Request) => Promise<Response>;
15
+ GET: (request: Request) => Promise<Response>;
16
+ PATCH: (request: Request) => Promise<Response>;
17
+ POST: (request: Request) => Promise<Response>;
18
+ PUT: (request: Request) => Promise<Response>;
27
19
  };
28
- export declare function createRouteHandler<E extends HttpRouteDeclaration>(route: E, handler: RouteHandler<E>, options?: CreateRouteHandlerOptions): RouteHandlerMap<E>;
29
- export declare const createRouterHandler: <const TContract extends WebContract>(contract: TContract, handlers: WebRouterHandlers<TContract>, options?: CreateRouteHandlerOptions) => RouterHandlerMap;
30
- export type { RequestBodySchema };
package/dist/server.js CHANGED
@@ -1,21 +1,12 @@
1
- import { initWeb, } from "@rest-rpc/web";
2
- export function createRouteHandler(route, handler, options) {
3
- const web = initWeb();
4
- const handle = web.createHandler(web.route(route, handler), {
1
+ import { createRouteHandler as createWebRouteHandler, route as webRoute, router as webRouter, } from "@rest-rpc/web";
2
+ export const route = (contract) => webRoute(contract);
3
+ export const router = (contract) => webRouter(contract);
4
+ export const createRouteHandler = (implementations, options) => {
5
+ const handle = createWebRouteHandler(implementations, {
5
6
  errorHandlers: options?.errorHandlers,
6
7
  parseBody: options?.parseBody,
7
8
  });
8
- return {
9
- [route.method]: (request) => handle(request, { request }),
10
- };
11
- }
12
- export const createRouterHandler = (contract, handlers, options) => {
13
- const web = initWeb();
14
- const handle = web.createHandler(web.router(contract, handlers), {
15
- errorHandlers: options?.errorHandlers,
16
- parseBody: options?.parseBody,
17
- });
18
- const nextHandler = (request) => handle(request, { request });
9
+ const nextHandler = (request) => handle(request, {});
19
10
  return {
20
11
  DELETE: nextHandler,
21
12
  GET: nextHandler,
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@rest-rpc/next",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.10",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/rest-rpc/rest-rpc.git"
9
+ },
6
10
  "main": "./dist/index.js",
7
11
  "types": "./dist/index.d.ts",
8
12
  "exports": {
@@ -14,13 +18,22 @@
14
18
  "files": [
15
19
  "dist"
16
20
  ],
21
+ "tsd": {
22
+ "directory": "test-d"
23
+ },
17
24
  "dependencies": {
18
- "@rest-rpc/core": "^0.1.0-beta.0",
19
- "@rest-rpc/web": "^0.1.0-beta.0"
25
+ "@rest-rpc/core": "^0.1.0-beta.10",
26
+ "@rest-rpc/web": "^0.1.0-beta.10"
27
+ },
28
+ "peerDependencies": {
29
+ "next": ">=15"
30
+ },
31
+ "devDependencies": {
32
+ "next": "16.3.1"
20
33
  },
21
34
  "scripts": {
22
35
  "build": "tsc -p tsconfig.build.json",
23
36
  "test": "node --test src/*.test.ts src/**/*.test.ts",
24
- "typecheck": "pnpm run build && tsc -p tsconfig.json"
37
+ "typecheck": "pnpm run build && tsc -p tsconfig.json && tsd"
25
38
  }
26
39
  }
package/dist/client.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import type { ApiClientFor, ApiClientOptions, Contract } from "@rest-rpc/core";
2
- import type { HttpRouteDeclaration } from "@rest-rpc/core/contract";
3
- type NextFetchOptions = RequestInit & {
4
- next?: {
5
- tags?: string[];
6
- [key: string]: unknown;
7
- };
8
- };
9
- export type NextClientFetchOptions = Omit<NextFetchOptions, "method" | "body" | "headers" | "signal">;
10
- export type NextClientOptions = Omit<ApiClientOptions, "fetchOptions"> & {
11
- automaticFetchTags?: {
12
- enabled: boolean;
13
- tagPrefix?: string;
14
- };
15
- fetchOptions?: NextClientFetchOptions;
16
- };
17
- export declare const initNextClient: <TContract extends Contract>(contract: TContract, options: NextClientOptions) => ApiClientFor<TContract>;
18
- export declare const getGeneratedTagsForRoute: (route: HttpRouteDeclaration, options?: {
19
- request?: Record<string, unknown>;
20
- tagPrefix?: string;
21
- }) => string[];
22
- export {};
package/dist/client.js DELETED
@@ -1,41 +0,0 @@
1
- import { getRouteCacheTags, initClient } from "@rest-rpc/core";
2
- const createRouteCacheTagsForUrl = (url, prefix) => {
3
- const parsed = new URL(url instanceof Request ? url.url : String(url));
4
- return Array.from(new Set([
5
- `${prefix ?? "rest-rpc"}:${parsed.pathname}${parsed.search}`,
6
- `${prefix ?? "rest-rpc"}:${parsed.pathname}`,
7
- ]));
8
- };
9
- const fetchWithTags = (fetchImpl, tagPrefix) => {
10
- return (url, init) => {
11
- if (init?.method !== "GET")
12
- return fetchImpl(url, init);
13
- const nextInit = init;
14
- const taggedInit = {
15
- ...nextInit,
16
- next: {
17
- ...nextInit.next,
18
- tags: [
19
- ...(nextInit.next?.tags ?? []),
20
- ...createRouteCacheTagsForUrl(url, tagPrefix),
21
- ],
22
- },
23
- };
24
- return fetchImpl(url, taggedInit);
25
- };
26
- };
27
- export const initNextClient = (contract, options) => {
28
- const { automaticFetchTags, fetch, ...clientOptions } = options;
29
- if (!automaticFetchTags?.enabled) {
30
- return initClient(contract, options);
31
- }
32
- const fetchImpl = fetch ?? ((url, init) => globalThis.fetch(url, init));
33
- return initClient(contract, {
34
- ...clientOptions,
35
- fetch: fetchWithTags(fetchImpl, automaticFetchTags.tagPrefix),
36
- });
37
- };
38
- export const getGeneratedTagsForRoute = (route, options) => getRouteCacheTags(route, {
39
- request: options?.request,
40
- prefix: options?.tagPrefix,
41
- });
package/dist/tags.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import type { HttpRouteDeclaration } from "@rest-rpc/core/contract";
2
- export declare const getUrlTag: (url: string) => string;
3
- export declare const getRouteTags: (route: HttpRouteDeclaration, request?: Record<string, unknown>) => string[];
4
- //# sourceMappingURL=tags.d.ts.map
package/dist/tags.js DELETED
@@ -1,11 +0,0 @@
1
- import { constructBaseRequest } from "@rest-rpc/core";
2
- const tagPrefix = "rest-rpc";
3
- export const getUrlTag = (url) => {
4
- const parsed = new URL(url, "http://rest-rpc.local");
5
- return `${tagPrefix}:${parsed.pathname}${parsed.search}`;
6
- };
7
- export const getRouteTags = (route, request) => {
8
- const { url } = constructBaseRequest("", route, request, "strip");
9
- return [getUrlTag(url)];
10
- };
11
- //# sourceMappingURL=tags.js.map