@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 +63 -27
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -3
- package/dist/server.d.ts +16 -27
- package/dist/server.js +6 -15
- package/package.json +17 -4
- package/dist/client.d.ts +0 -22
- package/dist/client.js +0 -41
- package/dist/tags.d.ts +0 -4
- package/dist/tags.js +0 -11
package/README.md
CHANGED
|
@@ -2,44 +2,80 @@
|
|
|
2
2
|
|
|
3
3
|
REST-shaped APIs with function-shaped TypeScript.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
+
Implement the contract on the server
|
|
17
41
|
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
|
|
42
|
+
```ts
|
|
43
|
+
const routes = router(api, {
|
|
44
|
+
todos: {
|
|
45
|
+
get({ id }) {
|
|
46
|
+
return getTodo(id);
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
21
50
|
|
|
22
|
-
|
|
51
|
+
registerRoutes(app, routes);
|
|
52
|
+
```
|
|
23
53
|
|
|
24
|
-
|
|
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
|
-
|
|
33
|
-
|
|
56
|
+
```ts
|
|
57
|
+
import { initClient } from "@rest-rpc/core";
|
|
58
|
+
import { api } from "./contract";
|
|
34
59
|
|
|
35
|
-
|
|
60
|
+
const client = initClient(api, {
|
|
61
|
+
baseUrl: "https://api.example.com",
|
|
62
|
+
});
|
|
36
63
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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 {
|
|
2
|
-
import { type CreateWebHandlerOptions, type
|
|
3
|
-
type
|
|
4
|
-
|
|
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
|
|
26
|
-
parseBody?:
|
|
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 {
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
19
|
-
"@rest-rpc/web": "^0.1.0-beta.
|
|
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
|