@hostwebhook/node 1.0.0 → 1.0.1
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 +167 -0
- package/llms.txt +167 -0
- package/package.json +4 -2
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @hostwebhook/node
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for [HostWebhook](https://hostwebhook.com) — verify webhook signatures and send webhooks through the platform.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @hostwebhook/node
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node.js 18+.
|
|
12
|
+
|
|
13
|
+
## Verify Webhook Signatures
|
|
14
|
+
|
|
15
|
+
HostWebhook signs every delivery with HMAC-SHA256. Use these helpers to verify authenticity and prevent replay attacks.
|
|
16
|
+
|
|
17
|
+
### Express
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import express from 'express';
|
|
21
|
+
import { expressMiddleware } from '@hostwebhook/node';
|
|
22
|
+
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
app.post('/webhooks',
|
|
26
|
+
express.raw({ type: 'application/json' }),
|
|
27
|
+
expressMiddleware(process.env.SIGNING_SECRET!),
|
|
28
|
+
(req, res) => {
|
|
29
|
+
const payload = JSON.parse(req.body.toString());
|
|
30
|
+
// process payload...
|
|
31
|
+
res.json({ received: true });
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> **Important:** Use `express.raw()` so `req.body` is a Buffer, not parsed JSON.
|
|
37
|
+
|
|
38
|
+
### NestJS
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { Controller, Post, UseGuards, Req } from '@nestjs/common';
|
|
42
|
+
import { createNestGuard } from '@hostwebhook/node';
|
|
43
|
+
|
|
44
|
+
const WebhookGuard = createNestGuard(process.env.SIGNING_SECRET!);
|
|
45
|
+
|
|
46
|
+
@Controller('webhooks')
|
|
47
|
+
export class WebhooksController {
|
|
48
|
+
@Post()
|
|
49
|
+
@UseGuards(WebhookGuard)
|
|
50
|
+
handle(@Req() req: Request) {
|
|
51
|
+
return { received: true };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Fastify
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import Fastify from 'fastify';
|
|
60
|
+
import { fastifyHook } from '@hostwebhook/node';
|
|
61
|
+
|
|
62
|
+
const app = Fastify();
|
|
63
|
+
app.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => done(null, body));
|
|
64
|
+
|
|
65
|
+
app.post('/webhooks', {
|
|
66
|
+
preHandler: fastifyHook(process.env.SIGNING_SECRET!),
|
|
67
|
+
}, (req, reply) => {
|
|
68
|
+
reply.send({ received: true });
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Manual Verification
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { verify, VerificationError } from '@hostwebhook/node';
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
verify(rawBody, request.headers, secret);
|
|
79
|
+
// signature valid
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (err instanceof VerificationError) {
|
|
82
|
+
// invalid — reject the request
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Options: `verify(payload, headers, secret, { maxAgeSec: 300 })` — default 300s (5 min). Set `0` to disable replay protection.
|
|
88
|
+
|
|
89
|
+
## Send Webhooks
|
|
90
|
+
|
|
91
|
+
Send webhook payloads through HostWebhook for reliable delivery with retries and monitoring.
|
|
92
|
+
|
|
93
|
+
### Class-based
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { HostWebhook } from '@hostwebhook/node';
|
|
97
|
+
|
|
98
|
+
const hw = new HostWebhook({ token: 'your_ingress_token' });
|
|
99
|
+
|
|
100
|
+
const { eventId } = await hw.send({ event: 'order.created', orderId: '123' });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Batch
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const results = await hw.sendBatch([
|
|
107
|
+
{ event: 'user.signup', userId: '1' },
|
|
108
|
+
{ event: 'user.signup', userId: '2' },
|
|
109
|
+
]);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Standalone Function
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { send } from '@hostwebhook/node';
|
|
116
|
+
|
|
117
|
+
const { eventId } = await send(
|
|
118
|
+
{ token: 'your_ingress_token' },
|
|
119
|
+
{ event: 'test', data: 'hello' },
|
|
120
|
+
);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
| Option | Type | Default | Description |
|
|
126
|
+
|--------|------|---------|-------------|
|
|
127
|
+
| `token` | `string` | *required* | Endpoint ingress token |
|
|
128
|
+
| `baseUrl` | `string` | `https://api.hostwebhook.com` | API base URL |
|
|
129
|
+
| `signingSecret` | `string` | — | Signs outgoing requests with HMAC-SHA256 |
|
|
130
|
+
| `defaultHeaders` | `Record<string, string>` | — | Headers added to every request |
|
|
131
|
+
| `timeoutMs` | `number` | `10000` | Request timeout in ms |
|
|
132
|
+
|
|
133
|
+
## Error Handling
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { SendError, VerificationError } from '@hostwebhook/node';
|
|
137
|
+
|
|
138
|
+
// Send errors
|
|
139
|
+
try {
|
|
140
|
+
await hw.send(payload);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
if (err instanceof SendError) {
|
|
143
|
+
console.log(err.statusCode, err.responseBody);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Verification errors
|
|
148
|
+
try {
|
|
149
|
+
verify(body, headers, secret);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (err instanceof VerificationError) {
|
|
152
|
+
console.log(err.message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Signature Format
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
X-HostWebhook-Signature: t=<timestamp_ms>,v1=<hmac_hex>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
HMAC computed as: `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
package/llms.txt
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @hostwebhook/node
|
|
2
|
+
|
|
3
|
+
> Official HostWebhook Node.js SDK — verify webhook signatures and send webhooks through the platform.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
npm install @hostwebhook/node
|
|
8
|
+
|
|
9
|
+
## Verify Signatures (receiving webhooks)
|
|
10
|
+
|
|
11
|
+
HostWebhook signs every delivery with HMAC-SHA256. Use these helpers to verify authenticity.
|
|
12
|
+
|
|
13
|
+
### Express
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import express from 'express';
|
|
17
|
+
import { expressMiddleware } from '@hostwebhook/node';
|
|
18
|
+
|
|
19
|
+
const app = express();
|
|
20
|
+
app.post('/webhooks',
|
|
21
|
+
express.raw({ type: 'application/json' }),
|
|
22
|
+
expressMiddleware(process.env.SIGNING_SECRET!),
|
|
23
|
+
(req, res) => {
|
|
24
|
+
const payload = JSON.parse(req.body.toString());
|
|
25
|
+
// process payload...
|
|
26
|
+
res.json({ received: true });
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
IMPORTANT: Use `express.raw()` so `req.body` is a Buffer, not parsed JSON.
|
|
32
|
+
|
|
33
|
+
### NestJS
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { Controller, Post, UseGuards, Req } from '@nestjs/common';
|
|
37
|
+
import { createNestGuard } from '@hostwebhook/node';
|
|
38
|
+
|
|
39
|
+
const WebhookGuard = createNestGuard(process.env.SIGNING_SECRET!);
|
|
40
|
+
|
|
41
|
+
@Controller('webhooks')
|
|
42
|
+
export class WebhooksController {
|
|
43
|
+
@Post()
|
|
44
|
+
@UseGuards(WebhookGuard)
|
|
45
|
+
handle(@Req() req: Request) {
|
|
46
|
+
return { received: true };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Fastify
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import Fastify from 'fastify';
|
|
55
|
+
import { fastifyHook } from '@hostwebhook/node';
|
|
56
|
+
|
|
57
|
+
const app = Fastify();
|
|
58
|
+
app.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => done(null, body));
|
|
59
|
+
|
|
60
|
+
app.post('/webhooks', {
|
|
61
|
+
preHandler: fastifyHook(process.env.SIGNING_SECRET!),
|
|
62
|
+
}, (req, reply) => {
|
|
63
|
+
reply.send({ received: true });
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Manual verification
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { verify, VerificationError } from '@hostwebhook/node';
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
verify(rawBody, request.headers, secret);
|
|
74
|
+
// signature is valid
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (err instanceof VerificationError) {
|
|
77
|
+
// invalid signature — reject
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
verify(payload, headers, secret, options?) — throws VerificationError if invalid.
|
|
83
|
+
VerifyOptions: { maxAgeSec?: number } — default 300 (5 min). Set 0 to disable replay protection.
|
|
84
|
+
|
|
85
|
+
## Send Webhooks (sending through HostWebhook)
|
|
86
|
+
|
|
87
|
+
### Class-based client
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { HostWebhook } from '@hostwebhook/node';
|
|
91
|
+
|
|
92
|
+
const hw = new HostWebhook({ token: 'your_ingress_token' });
|
|
93
|
+
|
|
94
|
+
// Send a single event
|
|
95
|
+
const { eventId } = await hw.send({ event: 'order.created', orderId: '123' });
|
|
96
|
+
|
|
97
|
+
// Send multiple events in parallel
|
|
98
|
+
const results = await hw.sendBatch([
|
|
99
|
+
{ event: 'user.signup', userId: '1' },
|
|
100
|
+
{ event: 'user.signup', userId: '2' },
|
|
101
|
+
]);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Standalone function (no class needed)
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { send } from '@hostwebhook/node';
|
|
108
|
+
|
|
109
|
+
const { eventId } = await send(
|
|
110
|
+
{ token: 'your_ingress_token' },
|
|
111
|
+
{ event: 'test', data: 'hello' },
|
|
112
|
+
);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Configuration
|
|
116
|
+
|
|
117
|
+
HostWebhookConfig: {
|
|
118
|
+
token: string (required — your endpoint's ingress token),
|
|
119
|
+
baseUrl?: string (default: https://api.hostwebhook.com),
|
|
120
|
+
signingSecret?: string (optional — signs outgoing requests with HMAC-SHA256),
|
|
121
|
+
defaultHeaders?: Record<string, string>,
|
|
122
|
+
timeoutMs?: number (default: 10000),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
SendOptions: {
|
|
126
|
+
headers?: Record<string, string>,
|
|
127
|
+
contentType?: string (default: application/json),
|
|
128
|
+
rawBody?: string | Buffer (skip JSON.stringify, send raw),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
SendResult: { eventId: string }
|
|
132
|
+
|
|
133
|
+
## Signature Format
|
|
134
|
+
|
|
135
|
+
Header: `X-HostWebhook-Signature: t=<timestamp_ms>,v1=<hmac_hex>`
|
|
136
|
+
HMAC computed as: `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`
|
|
137
|
+
|
|
138
|
+
## Error Handling
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { SendError, VerificationError } from '@hostwebhook/node';
|
|
142
|
+
|
|
143
|
+
// SendError — thrown when API returns non-2xx
|
|
144
|
+
try {
|
|
145
|
+
await hw.send(payload);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
if (err instanceof SendError) {
|
|
148
|
+
console.log(err.statusCode, err.responseBody);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// VerificationError — thrown when signature is invalid
|
|
153
|
+
try {
|
|
154
|
+
verify(body, headers, secret);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
if (err instanceof VerificationError) {
|
|
157
|
+
console.log(err.message); // e.g. "Signature mismatch"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Exports
|
|
163
|
+
|
|
164
|
+
Functions: verify, expressMiddleware, createNestGuard, fastifyHook, send
|
|
165
|
+
Classes: HostWebhook, SendError, VerificationError
|
|
166
|
+
Types: VerifyOptions, HostWebhookConfig, SendOptions, SendResult, WebhookHeaders
|
|
167
|
+
Helpers: parseHeaders (extract webhook metadata from headers)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hostwebhook/node",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Official HostWebhook SDK — verify signatures, parse headers, and send webhooks",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"llms.txt"
|
|
17
19
|
],
|
|
18
20
|
"scripts": {
|
|
19
21
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|