diff --git a/.changeset/little-tips-end.md b/.changeset/little-tips-end.md new file mode 100644 index 00000000..629a11fa --- /dev/null +++ b/.changeset/little-tips-end.md @@ -0,0 +1,5 @@ +--- +'@hono/trpc-server': patch +--- + +Initial release diff --git a/.github/workflows/ci-trpc-server.yml b/.github/workflows/ci-trpc-server.yml new file mode 100644 index 00000000..874c5180 --- /dev/null +++ b/.github/workflows/ci-trpc-server.yml @@ -0,0 +1,25 @@ +name: ci-trpc-server +on: + push: + branches: [main] + paths: + - 'packages/trpc-server/**' + pull_request: + branches: ['*'] + paths: + - 'packages/trpc-server/**' + +jobs: + ci: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./packages/trpc-server + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 18.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test diff --git a/package.json b/package.json index 39835382..199cb530 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "build:graphql-server": "yarn workspace @hono/graphql-server build", "build:sentry": "yarn workspace @hono/sentry build", "build:firebase-auth": "yarn workspace @hono/firebase-auth build", + "build:trpc-server": "yarn workspace @hono/trpc-server build", "build": "run-p build:*" }, "license": "MIT", diff --git a/packages/trpc-adapter/README.md b/packages/trpc-adapter/README.md deleted file mode 100644 index 2b2115e4..00000000 --- a/packages/trpc-adapter/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# tRPC Adapter Middleware for Hono - -## Usage - -```ts - -``` - -## Author - -## License - -MIT diff --git a/packages/trpc-adapter/package.json b/packages/trpc-adapter/package.json deleted file mode 100644 index 9c5c8b38..00000000 --- a/packages/trpc-adapter/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@honojs/trpc-adapter", - "version": "0.0.0", - "description": "tRPC Adapter Middleware for Hono", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "test": "jest", - "build": "rimraf dist && tsc", - "prerelease": "yarn build && yarn test", - "release": "yarn publish" - }, - "license": "MIT", - "private": false, - "publishConfig": { - "registry": "https://registry.npmjs.org", - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/honojs/middleware.git" - }, - "homepage": "https://honojs.dev", - "dependencies": { - "hono": "^2.3.0" - } -} diff --git a/packages/trpc-adapter/src/index.test.ts b/packages/trpc-adapter/src/index.test.ts deleted file mode 100644 index 1d418587..00000000 --- a/packages/trpc-adapter/src/index.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Hono } from 'hono' -import { trpcAdapter } from '../src' - -describe('tRPC Adapter Middleware', () => { - const app = new Hono() - - app.use('/trpc/*', trpcAdapter()) - app.get('/trpc', (c) => c.text('Here is dummy endpoint')) - - it('Should return 200 response', async () => { - const res = await app.request('http://localhost/trpc') - expect(res).not.toBeNull() - expect(res.status).toBe(200) - }) -}) diff --git a/packages/trpc-adapter/src/index.ts b/packages/trpc-adapter/src/index.ts deleted file mode 100644 index 5c6f9eac..00000000 --- a/packages/trpc-adapter/src/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Handler } from 'hono' - -export const trpcAdapter = (): Handler => { - return async (_c, next) => { - await next() - } -} diff --git a/packages/trpc-server/README.md b/packages/trpc-server/README.md new file mode 100644 index 00000000..24750ef3 --- /dev/null +++ b/packages/trpc-server/README.md @@ -0,0 +1,76 @@ +# tRPC Server Middleware for Hono + +tRPC Server Middleware adapts a [tRPC](https://trpc.io) server as middleware for Hono. +Hono works on almost any JavaScript runtime, including Cloudflare Workers, Deno, and Bun. So, with this middleware, the same code will run as tRPC server. + +## Install + +``` +npm install @hono/trpc-server +``` + +## Usage + +Router: + +```ts +import { initTRPC } from '@trpc/server' +import { z } from 'zod' + +const t = initTRPC.create() + +const publicProcedure = t.procedure +const router = t.router + +export const appRouter = router({ + hello: publicProcedure.input(z.string().nullish()).query(({ input }) => { + return `Hello ${input ?? 'World'}!` + }), +}) + +export type AppRouter = typeof appRouter +``` + +Hono app using tRPC Server Middleware: + +```ts +import { Hono } from 'hono' +import { trpcServer } from '@hono/trpc-server' // Deno 'npm:@hono/trpc-server' +import { appRouter } from './router' + +const app = new Hono() + +app.use( + '/trpc/*', + trpcServer({ + router: appRouter, + }) +) + +export default app +``` + +Client: + +```ts +import { createTRPCProxyClient, httpBatchLink } from '@trpc/client' +import type { AppRouter } from './router' + +const client = createTRPCProxyClient({ + links: [ + httpBatchLink({ + url: 'http://localhost:8787/trpc', + }), + ], +}) + +console.log(await client.hello.query('Hono')) +``` + +## Author + +Yusuke Wada + +## License + +MIT diff --git a/packages/trpc-adapter/jest.config.js b/packages/trpc-server/jest.config.js similarity index 100% rename from packages/trpc-adapter/jest.config.js rename to packages/trpc-server/jest.config.js diff --git a/packages/trpc-server/package.json b/packages/trpc-server/package.json new file mode 100644 index 00000000..9b43ac97 --- /dev/null +++ b/packages/trpc-server/package.json @@ -0,0 +1,42 @@ +{ + "name": "@hono/trpc-server", + "version": "0.0.0", + "description": "tRPC Server Middleware for Hono", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "test": "jest", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:esm": "tsc -p tsconfig.esm.json", + "build": "rimraf dist && yarn build:cjs && yarn build:esm", + "prerelease": "yarn build && yarn test", + "release": "yarn publish" + }, + "license": "MIT", + "private": false, + "publishConfig": { + "registry": "https://registry.npmjs.org", + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/honojs/middleware.git" + }, + "homepage": "https://honojs.dev", + "peerDependencies": { + "@trpc/server": "^10.10.0", + "hono": "^2.7.5" + }, + "devDependencies": { + "@trpc/server": "^10.10.0", + "hono": "^2.7.5", + "zod": "^3.20.2" + }, + "engines": { + "node": ">=16.0.0" + } +} \ No newline at end of file diff --git a/packages/trpc-server/src/index.ts b/packages/trpc-server/src/index.ts new file mode 100644 index 00000000..45af0f88 --- /dev/null +++ b/packages/trpc-server/src/index.ts @@ -0,0 +1,19 @@ +import type { AnyRouter } from '@trpc/server' +import { fetchRequestHandler } from '@trpc/server/adapters/fetch' +import type { MiddlewareHandler } from 'hono' + +type tRPCOptions = { + endpoint?: string + router: AnyRouter +} + +export const trpcServer = ({ router, endpoint = '/trpc' }: tRPCOptions): MiddlewareHandler => { + return async (c) => { + const res = fetchRequestHandler({ + endpoint: endpoint, + req: c.req, + router: router, + }) + return res + } +} diff --git a/packages/trpc-server/test/index.test.ts b/packages/trpc-server/test/index.test.ts new file mode 100644 index 00000000..99e7f276 --- /dev/null +++ b/packages/trpc-server/test/index.test.ts @@ -0,0 +1,43 @@ +import { initTRPC } from '@trpc/server' +import { Hono } from 'hono' +import { z } from 'zod' +import { trpcServer } from '../src' + +describe('tRPC Adapter Middleware', () => { + const t = initTRPC.create() + + const publicProcedure = t.procedure + const router = t.router + + const appRouter = router({ + hello: publicProcedure.input(z.string().nullish()).query(({ input }) => { + return `Hello ${input ?? 'World'}` + }), + }) + + const app = new Hono() + + app.use( + '/trpc/*', + trpcServer({ + router: appRouter, + }) + ) + + it('Should return 200 response', async () => { + const searchParams = new URLSearchParams({ + input: JSON.stringify({ '0': 'Hono' }), + batch: '1', + }) + const req = new Request(`http://localhost/trpc/hello?${searchParams.toString()}`) + const res = await app.request(req) + expect(res.status).toBe(200) + expect(await res.json()).toEqual([ + { + result: { + data: 'Hello Hono', + }, + }, + ]) + }) +}) diff --git a/packages/trpc-server/tsconfig.cjs.json b/packages/trpc-server/tsconfig.cjs.json new file mode 100644 index 00000000..b8bf50ee --- /dev/null +++ b/packages/trpc-server/tsconfig.cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "declaration": false, + "outDir": "./dist/cjs" + } +} \ No newline at end of file diff --git a/packages/trpc-server/tsconfig.esm.json b/packages/trpc-server/tsconfig.esm.json new file mode 100644 index 00000000..8130f1a5 --- /dev/null +++ b/packages/trpc-server/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "declaration": true, + "outDir": "./dist/esm" + } +} \ No newline at end of file diff --git a/packages/trpc-adapter/tsconfig.json b/packages/trpc-server/tsconfig.json similarity index 83% rename from packages/trpc-adapter/tsconfig.json rename to packages/trpc-server/tsconfig.json index acfcd843..6c1a3990 100644 --- a/packages/trpc-adapter/tsconfig.json +++ b/packages/trpc-server/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", - "outDir": "./dist", }, "include": [ "src/**/*.ts" diff --git a/yarn.lock b/yarn.lock index 0e7259e6..ea8fa452 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1511,6 +1511,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@trpc/server@^10.10.0": + version "10.10.0" + resolved "https://registry.yarnpkg.com/@trpc/server/-/server-10.10.0.tgz#0b494335140d4fd5e1452f7b57dcc9b8886720a4" + integrity sha512-tCTqcqBT+3nebYFTHtwM877qo5xQPtVlptxKdUzMVWleWT4lFTL4oddk45qVURToci2iMbVJjd4jQU9y9/XwlQ== + "@types/acorn@^4.0.0": version "4.0.6" resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" @@ -5185,12 +5190,7 @@ hono@3.0.0-rc.9: resolved "https://registry.yarnpkg.com/hono/-/hono-3.0.0-rc.9.tgz#6077bc67fce850dca99cd58452bda21ff8984da8" integrity sha512-gW/I51Uay6RhrDhtRN/3WTg7ALT3VRdk0NZBdGpkEOgF+TrzFwmWHDbzHUPa9MffiXSlrkFtMmJVvdKO3Iab8Q== -hono@^2.3.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/hono/-/hono-2.6.2.tgz#1e32a4b4b3bf557a8aa87e392b00b505a1ec1fdd" - integrity sha512-Y4Uv6dPPx6u7TNoWN245tLw8jycBf97w0FJGJoEMdgdBqQEv1dqIvg5IfDhiYMkimSIt4SXxmMgiyPf2wzwFWQ== - -hono@^2.6.1: +hono@^2.6.1, hono@^2.7.5: version "2.7.5" resolved "https://registry.yarnpkg.com/hono/-/hono-2.7.5.tgz#2b0c39d39a057575962a7025373be4304dedca9c" integrity sha512-UYrhUT1Xtalh7Xw/YGXee25wlWMiVIRfr0FbAJMEMyRpX9TFZ/6JKmVdCK7ioRnhAw7PfWBasjSBhdNXQ8/okQ== @@ -10806,6 +10806,11 @@ zod@3.19.1: resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473" integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA== +zod@^3.20.2: + version "3.20.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807" + integrity sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ== + zwitch@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"