@kiyasov/platform-hono 1.0.1 → 1.0.3
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/.yarn/install-state.gz +0 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/src/drivers/graphql.driver.d.ts +16 -0
- package/dist/cjs/src/drivers/graphql.driver.js +99 -0
- package/dist/cjs/src/drivers/graphql.driver.js.map +1 -0
- package/dist/cjs/src/drivers/index.d.ts +1 -0
- package/dist/cjs/src/drivers/index.js +18 -0
- package/dist/cjs/src/drivers/index.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/drivers/graphql.driver.d.ts +16 -0
- package/dist/esm/src/drivers/graphql.driver.js +95 -0
- package/dist/esm/src/drivers/graphql.driver.js.map +1 -0
- package/dist/esm/src/drivers/index.d.ts +1 -0
- package/dist/esm/src/drivers/index.js +2 -0
- package/dist/esm/src/drivers/index.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/index.ts +1 -0
- package/package.json +5 -1
- package/src/drivers/graphql.driver.ts +126 -0
- package/src/drivers/index.ts +1 -0
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiyasov/platform-hono",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Nest adapter for Hono",
|
|
5
5
|
"author": "Islam Kiiasov",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,13 +16,17 @@
|
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@hono/graphql-server": "^0.4.3",
|
|
19
20
|
"@hono/node-server": "^1.11.2",
|
|
20
21
|
"hono": "^4.4.3"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
24
|
+
"@apollo/server": "^4.10.4",
|
|
25
|
+
"@nestjs/apollo": "^12.1.0",
|
|
23
26
|
"@nestjs/cli": "^10.3.2",
|
|
24
27
|
"@nestjs/common": "^10.3.8",
|
|
25
28
|
"@nestjs/core": "^10.3.8",
|
|
29
|
+
"@nestjs/graphql": "^12.1.1",
|
|
26
30
|
"@swc/cli": "^0.3.12",
|
|
27
31
|
"@swc/core": "^1.5.24",
|
|
28
32
|
"reflect-metadata": "^0.2.2",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { ApolloServer, BaseContext, HeaderMap } from '@apollo/server';
|
|
2
|
+
import { AbstractGraphQLDriver } from '@nestjs/graphql';
|
|
3
|
+
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
|
|
4
|
+
import { ApolloDriverConfig } from '@nestjs/apollo';
|
|
5
|
+
import { Context, HonoRequest } from 'hono';
|
|
6
|
+
import { StatusCode } from 'hono/utils/http-status';
|
|
7
|
+
import { Logger } from '@nestjs/common';
|
|
8
|
+
|
|
9
|
+
export class HonoGraphQLDriver<
|
|
10
|
+
T extends Record<string, any> = ApolloDriverConfig,
|
|
11
|
+
> extends AbstractGraphQLDriver {
|
|
12
|
+
protected apolloServer: ApolloServer<BaseContext>;
|
|
13
|
+
|
|
14
|
+
get instance(): ApolloServer<BaseContext> {
|
|
15
|
+
return this.apolloServer;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async start(options: T): Promise<void> {
|
|
19
|
+
const { httpAdapter } = this.httpAdapterHost;
|
|
20
|
+
const platformName = httpAdapter.getType();
|
|
21
|
+
|
|
22
|
+
if (platformName !== 'hono') {
|
|
23
|
+
throw new Error('This driver is only compatible with the Hono platform');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return this.registerHono(options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected async registerHono(
|
|
30
|
+
options: T,
|
|
31
|
+
{ preStartHook }: { preStartHook?: () => void } = {},
|
|
32
|
+
) {
|
|
33
|
+
const { path, typeDefs, resolvers, schema } = options;
|
|
34
|
+
const { httpAdapter } = this.httpAdapterHost;
|
|
35
|
+
const app = httpAdapter.getInstance();
|
|
36
|
+
const drainHttpServerPlugin = ApolloServerPluginDrainHttpServer({
|
|
37
|
+
httpServer: httpAdapter.getHttpServer(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
preStartHook?.();
|
|
41
|
+
|
|
42
|
+
const server = new ApolloServer({
|
|
43
|
+
typeDefs,
|
|
44
|
+
resolvers,
|
|
45
|
+
schema,
|
|
46
|
+
...options,
|
|
47
|
+
plugins: (options.plugins || []).concat(drainHttpServerPlugin),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await server.start();
|
|
51
|
+
|
|
52
|
+
app.use(path, async (ctx: Context) => {
|
|
53
|
+
const bodyData = await this.parseBody(ctx.req);
|
|
54
|
+
|
|
55
|
+
const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({
|
|
56
|
+
httpGraphQLRequest: {
|
|
57
|
+
body: bodyData,
|
|
58
|
+
method: ctx.req.method,
|
|
59
|
+
headers: this.httpHeadersToMap(ctx.req.raw.headers),
|
|
60
|
+
search: new URL(ctx.req.url).search,
|
|
61
|
+
},
|
|
62
|
+
context: options?.context ?? (() => Promise.resolve({} as BaseContext)),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const { headers, body, status } = httpGraphQLResponse;
|
|
66
|
+
|
|
67
|
+
for (const [headerKey, headerValue] of headers) {
|
|
68
|
+
ctx.header(headerKey, headerValue);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ctx.status(status === undefined ? 200 : (status as StatusCode));
|
|
72
|
+
|
|
73
|
+
if (body.kind === 'complete') {
|
|
74
|
+
return ctx.body(body.string);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const readableStream = new ReadableStream({
|
|
78
|
+
async start(controller) {
|
|
79
|
+
for await (const chunk of body.asyncIterator) {
|
|
80
|
+
controller.enqueue(new TextEncoder().encode(chunk));
|
|
81
|
+
}
|
|
82
|
+
controller.close();
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return new Response(readableStream, {
|
|
87
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
this.apolloServer = server;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public stop() {
|
|
95
|
+
return this.apolloServer?.stop();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private httpHeadersToMap(headers: Headers) {
|
|
99
|
+
const map = new HeaderMap();
|
|
100
|
+
headers.forEach((value, key) => map.set(key, value));
|
|
101
|
+
return map;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private async parseBody(req: HonoRequest): Promise<Record<string, unknown>> {
|
|
105
|
+
const contentType = req.header('content-type');
|
|
106
|
+
if (contentType === 'application/graphql')
|
|
107
|
+
return { query: await req.text() };
|
|
108
|
+
if (contentType === 'application/json')
|
|
109
|
+
return req.json().catch(this.logError);
|
|
110
|
+
if (contentType === 'application/x-www-form-urlencoded')
|
|
111
|
+
return this.parseFormURL(req);
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private logError(e: unknown): void {
|
|
116
|
+
if (e instanceof Error) {
|
|
117
|
+
Logger.error(e.stack || e.message);
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`POST body sent invalid JSON: ${e}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async parseFormURL(req: HonoRequest) {
|
|
123
|
+
const searchParams = new URLSearchParams(await req.text());
|
|
124
|
+
return Object.fromEntries(searchParams.entries());
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./graphql.driver";
|