diff --git a/README.md b/README.md index 67bb6bfa..37b1d116 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,16 @@ export default { }; ``` +##### With [`Netlify Functions`](https://docs.netlify.com/functions/overview/) + +```js +import { createHandler } from 'graphql-http/lib/use/@netlify/functions'; // yarn add @netlify/functions +import { schema } from './previous-step'; + +// Create the GraphQL over HTTP native fetch handler +export const handler = createHandler({ schema }); +``` + #### Use the client ```js diff --git a/docs/README.md b/docs/README.md index 6eabc3ba..5ced85bf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,6 +12,7 @@ graphql-http - [client](modules/client.md) - [common](modules/common.md) - [handler](modules/handler.md) +- [use/@netlify/functions](modules/use__netlify_functions.md) - [use/express](modules/use_express.md) - [use/fastify](modules/use_fastify.md) - [use/fetch](modules/use_fetch.md) diff --git a/docs/modules/use__netlify_functions.md b/docs/modules/use__netlify_functions.md new file mode 100644 index 00000000..2362315c --- /dev/null +++ b/docs/modules/use__netlify_functions.md @@ -0,0 +1,51 @@ +[graphql-http](../README.md) / use/@netlify/functions + +# Module: use/@netlify/functions + +## Table of contents + +### Type Aliases + +- [HandlerOptions](use__netlify_functions.md#handleroptions) + +### Functions + +- [createHandler](use__netlify_functions.md#createhandler) + +## Server/@netlify/functions + +### HandlerOptions + +Ƭ **HandlerOptions**<`Context`\>: [`HandlerOptions`](../interfaces/handler.HandlerOptions.md)<`NetlifyHandlerEvent`, `NetlifyHandlerContext`, `Context`\> + +Handler options when using the netlify adapter + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `Context` | extends [`OperationContext`](handler.md#operationcontext) = `undefined` | + +___ + +### createHandler + +▸ **createHandler**<`Context`\>(`options`): `NetlifyHandler` + +Create a GraphQL over HTTP spec compliant request handler for netlify functions + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `Context` | extends [`OperationContext`](handler.md#operationcontext) = `undefined` | + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `options` | [`HandlerOptions`](use__netlify_functions.md#handleroptions)<`Context`\> | + +#### Returns + +`NetlifyHandler` diff --git a/package.json b/package.json index 568741a1..30d135d4 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,11 @@ "require": "./lib/use/fastify.js", "import": "./lib/use/fastify.mjs" }, + "./lib/use/@netlify/functions": { + "types": "./lib/use/@netlify/functions.d.ts", + "require": "./lib/use/@netlify/functions.js", + "import": "./lib/use/@netlify/functions.mjs" + }, "./lib/use/koa": { "types": "./lib/use/koa.d.ts", "require": "./lib/use/koa.js", @@ -112,6 +117,7 @@ }, "devDependencies": { "@cspell/cspell-types": "^6.31.1", + "@netlify/functions": "^1.6.0", "@rollup/plugin-terser": "^0.4.3", "@rollup/plugin-typescript": "^11.1.2", "@semantic-release/changelog": "^6.0.3", diff --git a/src/use/@netlify/functions.ts b/src/use/@netlify/functions.ts new file mode 100644 index 00000000..4fa66780 --- /dev/null +++ b/src/use/@netlify/functions.ts @@ -0,0 +1,56 @@ +import { + createHandler as createRawHandler, + HandlerOptions as RawHandlerOptions, + OperationContext, +} from '../../handler'; + +import type { + Handler as NetlifyHandler, + HandlerEvent as NetlifyHandlerEvent, + HandlerContext as NetlifyHandlerContext, +} from '@netlify/functions'; + +/** + * Handler options when using the netlify adapter + * + * @category Server/@netlify/functions + */ +export type HandlerOptions = + RawHandlerOptions; + +/** + * Create a GraphQL over HTTP spec compliant request handler for netlify functions + * + * @category Server/@netlify/functions + */ +export function createHandler( + options: HandlerOptions, +): NetlifyHandler { + const handler = createRawHandler(options); + return async function handleRequest(req, ctx) { + try { + const [body, init] = await handler({ + method: req.httpMethod, + url: req.rawUrl, + headers: req.headers, + body: req.body, + raw: req, + context: ctx, + }); + return { + // if body is null, return undefined + body: body ?? undefined, + statusCode: init.status, + }; + } catch (err) { + // The handler shouldnt throw errors. + // If you wish to handle them differently, consider implementing your own request handler. + console.error( + 'Internal error occurred during request handling. ' + + 'Please check your implementation.', + err, + ); + return { statusCode: 500 }; + } + }; +} diff --git a/yarn.lock b/yarn.lock index afb46998..22a08a14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3073,6 +3073,15 @@ __metadata: languageName: node linkType: hard +"@netlify/functions@npm:^1.6.0": + version: 1.6.0 + resolution: "@netlify/functions@npm:1.6.0" + dependencies: + is-promise: ^4.0.0 + checksum: ecff9a1161b2df94d139431e7a2b42d9790cf4ec250ad3aec57935cc1b1e78beccfe855c6e5522311baefce84b91ed690904aedf02b2511eaaafdc8f6daab75e + languageName: node + linkType: hard + "@nicolo-ribaudo/semver-v6@npm:^6.3.3": version: 6.3.3 resolution: "@nicolo-ribaudo/semver-v6@npm:6.3.3" @@ -7499,6 +7508,7 @@ __metadata: resolution: "graphql-http@workspace:." dependencies: "@cspell/cspell-types": ^6.31.1 + "@netlify/functions": ^1.6.0 "@rollup/plugin-terser": ^0.4.3 "@rollup/plugin-typescript": ^11.1.2 "@semantic-release/changelog": ^6.0.3 @@ -8278,6 +8288,13 @@ __metadata: languageName: node linkType: hard +"is-promise@npm:^4.0.0": + version: 4.0.0 + resolution: "is-promise@npm:4.0.0" + checksum: 0b46517ad47b00b6358fd6553c83ec1f6ba9acd7ffb3d30a0bf519c5c69e7147c132430452351b8a9fc198f8dd6c4f76f8e6f5a7f100f8c77d57d9e0f4261a8a + languageName: node + linkType: hard + "is-property@npm:^1.0.2": version: 1.0.2 resolution: "is-property@npm:1.0.2"