@xenterprises/fastify-xhubspot 1.0.0

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/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@xenterprises/fastify-xhubspot",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "Fastify plugin for HubSpot CRM integration with contact management, engagement tracking, and custom objects support. Ideal for third-party portals managing contacts, companies, deals, and engagement notes.",
6
+ "main": "src/xHubspot.js",
7
+ "exports": {
8
+ ".": "./src/xHubspot.js",
9
+ "./types": "./index.d.ts"
10
+ },
11
+ "files": [
12
+ "src/",
13
+ "index.d.ts",
14
+ "LICENSE",
15
+ "README.md",
16
+ "CHANGELOG.md",
17
+ "SECURITY.md"
18
+ ],
19
+ "scripts": {
20
+ "start": "fastify start -l info server/app.js",
21
+ "dev": "fastify start -w -l info -P server/app.js",
22
+ "test": "node --test test/xHubspot.test.js",
23
+ "test:watch": "node --watch --test test/xHubspot.test.js"
24
+ },
25
+ "engines": {
26
+ "node": ">=20.0.0",
27
+ "npm": ">=10.0.0"
28
+ },
29
+ "keywords": [
30
+ "fastify",
31
+ "fastify-plugin",
32
+ "hubspot",
33
+ "hubspot-api",
34
+ "crm",
35
+ "contacts",
36
+ "companies",
37
+ "deals",
38
+ "engagement",
39
+ "engagement-notes",
40
+ "custom-objects",
41
+ "contact-management",
42
+ "third-party-portal",
43
+ "api",
44
+ "plugin",
45
+ "nodejs",
46
+ "typescript"
47
+ ],
48
+ "homepage": "https://github.com/xenterprises/fastify-xhubspot#readme",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/xenterprises/fastify-xhubspot.git"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/xenterprises/fastify-xhubspot/issues"
55
+ },
56
+ "author": {
57
+ "name": "Tim Mushen",
58
+ "email": "tim@example.com",
59
+ "url": "https://example.com"
60
+ },
61
+ "license": "ISC",
62
+ "funding": {
63
+ "type": "individual",
64
+ "url": "https://github.com/sponsors/timmushen"
65
+ },
66
+ "devDependencies": {
67
+ "@types/node": "^22.7.4",
68
+ "fastify": "^5.1.0",
69
+ "fastify-plugin": "^5.0.0",
70
+ "typescript": "^5.6.3"
71
+ },
72
+ "dependencies": {
73
+ "@hubspot/api-client": "^11.2.0",
74
+ "fastify-plugin": "^5.0.0"
75
+ },
76
+ "peerDependencies": {
77
+ "fastify": "^5.0.0"
78
+ },
79
+ "optionalDependencies": {
80
+ "ioredis": "^5.3.0"
81
+ },
82
+ "publishConfig": {
83
+ "access": "public"
84
+ }
85
+ }
package/src/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // src/index.js
2
+ export { default } from "./xHubspot.js";
3
+ export { default as xHubspot } from "./xHubspot.js";
@@ -0,0 +1,138 @@
1
+ // src/services/companies.js
2
+ export function setupCompanies(hubspot, fastify, options = {}) {
3
+ const { logRequests } = options;
4
+
5
+ return {
6
+ async create(companyData) {
7
+ try {
8
+ if (logRequests) fastify.log.debug("Creating company:", companyData);
9
+ const response = await hubspot.crm.companies.basicApi.create({
10
+ properties: companyData,
11
+ });
12
+ return { id: response.id, properties: response.properties };
13
+ } catch (error) {
14
+ fastify.log.error("Failed to create company:", error.message);
15
+ throw error;
16
+ }
17
+ },
18
+
19
+ async getById(companyId, properties = []) {
20
+ try {
21
+ if (logRequests) fastify.log.debug(`Getting company: ${companyId}`);
22
+ const response = await hubspot.crm.companies.basicApi.getById(
23
+ companyId,
24
+ properties
25
+ );
26
+ return { id: response.id, properties: response.properties };
27
+ } catch (error) {
28
+ fastify.log.error(`Failed to get company ${companyId}:`, error.message);
29
+ throw error;
30
+ }
31
+ },
32
+
33
+ async update(companyId, properties) {
34
+ try {
35
+ if (logRequests) fastify.log.debug(`Updating company ${companyId}`);
36
+ const response = await hubspot.crm.companies.basicApi.update(
37
+ companyId,
38
+ { properties }
39
+ );
40
+ return { id: response.id, properties: response.properties };
41
+ } catch (error) {
42
+ fastify.log.error(`Failed to update company ${companyId}:`, error.message);
43
+ throw error;
44
+ }
45
+ },
46
+
47
+ async delete(companyId) {
48
+ try {
49
+ if (logRequests) fastify.log.debug(`Deleting company: ${companyId}`);
50
+ await hubspot.crm.companies.basicApi.archive(companyId);
51
+ return true;
52
+ } catch (error) {
53
+ fastify.log.error(`Failed to delete company ${companyId}:`, error.message);
54
+ throw error;
55
+ }
56
+ },
57
+
58
+ async list(options = {}) {
59
+ try {
60
+ const { limit = 100, after } = options;
61
+ if (logRequests) fastify.log.debug("Listing companies");
62
+ const response = await hubspot.crm.companies.basicApi.getPage(
63
+ limit,
64
+ after,
65
+ undefined,
66
+ []
67
+ );
68
+ return {
69
+ companies: (response.results || []).map(c => ({
70
+ id: c.id,
71
+ properties: c.properties,
72
+ })),
73
+ paging: response.paging,
74
+ };
75
+ } catch (error) {
76
+ fastify.log.error("Failed to list companies:", error.message);
77
+ throw error;
78
+ }
79
+ },
80
+
81
+ async search(property, value) {
82
+ try {
83
+ if (logRequests) fastify.log.debug(`Searching companies: ${property}=${value}`);
84
+ const response = await hubspot.crm.companies.searchApi.doSearch({
85
+ filterGroups: [
86
+ {
87
+ filters: [
88
+ {
89
+ propertyName: property,
90
+ operator: "EQ",
91
+ value: String(value),
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ limit: 100,
97
+ });
98
+ return (response.results || []).map(c => ({
99
+ id: c.id,
100
+ properties: c.properties,
101
+ }));
102
+ } catch (error) {
103
+ fastify.log.error("Failed to search companies:", error.message);
104
+ throw error;
105
+ }
106
+ },
107
+
108
+ async batchCreate(companies) {
109
+ try {
110
+ if (logRequests) fastify.log.debug(`Creating ${companies.length} companies`);
111
+ const response = await hubspot.crm.companies.batchApi.create({
112
+ inputs: companies.map(c => ({ properties: c })),
113
+ });
114
+ return response.results.map(r => ({
115
+ id: r.id,
116
+ properties: r.properties,
117
+ }));
118
+ } catch (error) {
119
+ fastify.log.error("Failed to batch create companies:", error.message);
120
+ throw error;
121
+ }
122
+ },
123
+
124
+ async getAssociations(companyId, associationType) {
125
+ try {
126
+ if (logRequests) fastify.log.debug(`Getting associations for company ${companyId}`);
127
+ const response = await hubspot.crm.companies.associationsApi.getAll(
128
+ companyId,
129
+ associationType
130
+ );
131
+ return response.results.map(a => ({ id: a.id, type: a.type }));
132
+ } catch (error) {
133
+ fastify.log.error(`Failed to get company associations:`, error.message);
134
+ throw error;
135
+ }
136
+ },
137
+ };
138
+ }
@@ -0,0 +1,327 @@
1
+ // src/services/contacts.js
2
+ export function setupContacts(hubspot, fastify, options = {}) {
3
+ const { logRequests } = options;
4
+
5
+ return {
6
+ /**
7
+ * Create a new contact
8
+ * @param {Object} contactData - Contact properties
9
+ * @returns {Promise<Object>} Created contact details
10
+ */
11
+ async create(contactData) {
12
+ try {
13
+ if (logRequests) {
14
+ fastify.log.debug("Creating contact:", contactData);
15
+ }
16
+
17
+ const response = await hubspot.crm.contacts.basicApi.create({
18
+ properties: contactData,
19
+ });
20
+
21
+ return {
22
+ id: response.id,
23
+ properties: response.properties,
24
+ createdAt: response.createdAt,
25
+ };
26
+ } catch (error) {
27
+ fastify.log.error("Failed to create contact:", error.message);
28
+ throw error;
29
+ }
30
+ },
31
+
32
+ /**
33
+ * Get contact by ID
34
+ * @param {string} contactId - Contact HubSpot ID
35
+ * @param {Array<string>} properties - Properties to retrieve
36
+ * @returns {Promise<Object>} Contact details
37
+ */
38
+ async getById(contactId, properties = []) {
39
+ try {
40
+ if (logRequests) {
41
+ fastify.log.debug(`Getting contact: ${contactId}`);
42
+ }
43
+
44
+ const response = await hubspot.crm.contacts.basicApi.getById(
45
+ contactId,
46
+ properties,
47
+ undefined,
48
+ false
49
+ );
50
+
51
+ return {
52
+ id: response.id,
53
+ properties: response.properties,
54
+ createdAt: response.createdAt,
55
+ updatedAt: response.updatedAt,
56
+ };
57
+ } catch (error) {
58
+ fastify.log.error(`Failed to get contact ${contactId}:`, error.message);
59
+ throw error;
60
+ }
61
+ },
62
+
63
+ /**
64
+ * Get contact by email
65
+ * @param {string} email - Contact email address
66
+ * @param {Array<string>} properties - Properties to retrieve
67
+ * @returns {Promise<Object>} Contact details
68
+ */
69
+ async getByEmail(email, properties = []) {
70
+ try {
71
+ if (logRequests) {
72
+ fastify.log.debug(`Getting contact by email: ${email}`);
73
+ }
74
+
75
+ const response = await hubspot.crm.contacts.basicApi.getById(
76
+ email,
77
+ properties,
78
+ "email",
79
+ false
80
+ );
81
+
82
+ return {
83
+ id: response.id,
84
+ properties: response.properties,
85
+ email,
86
+ };
87
+ } catch (error) {
88
+ fastify.log.error(`Failed to get contact by email ${email}:`, error.message);
89
+ throw error;
90
+ }
91
+ },
92
+
93
+ /**
94
+ * Update contact
95
+ * @param {string} contactId - Contact HubSpot ID
96
+ * @param {Object} properties - Properties to update
97
+ * @returns {Promise<Object>} Updated contact details
98
+ */
99
+ async update(contactId, properties) {
100
+ try {
101
+ if (logRequests) {
102
+ fastify.log.debug(`Updating contact ${contactId}:`, properties);
103
+ }
104
+
105
+ const response = await hubspot.crm.contacts.basicApi.update(
106
+ contactId,
107
+ { properties }
108
+ );
109
+
110
+ return {
111
+ id: response.id,
112
+ properties: response.properties,
113
+ updatedAt: response.updatedAt,
114
+ };
115
+ } catch (error) {
116
+ fastify.log.error(`Failed to update contact ${contactId}:`, error.message);
117
+ throw error;
118
+ }
119
+ },
120
+
121
+ /**
122
+ * Delete contact
123
+ * @param {string} contactId - Contact HubSpot ID
124
+ * @returns {Promise<boolean>} Deletion status
125
+ */
126
+ async delete(contactId) {
127
+ try {
128
+ if (logRequests) {
129
+ fastify.log.debug(`Deleting contact: ${contactId}`);
130
+ }
131
+
132
+ await hubspot.crm.contacts.basicApi.archive(contactId);
133
+ return true;
134
+ } catch (error) {
135
+ fastify.log.error(`Failed to delete contact ${contactId}:`, error.message);
136
+ throw error;
137
+ }
138
+ },
139
+
140
+ /**
141
+ * List contacts
142
+ * @param {Object} options - List options (limit, after, sort)
143
+ * @returns {Promise<Array>} List of contacts
144
+ */
145
+ async list(options = {}) {
146
+ try {
147
+ const { limit = 100, after, sort } = options;
148
+
149
+ if (logRequests) {
150
+ fastify.log.debug("Listing contacts:", options);
151
+ }
152
+
153
+ const response = await hubspot.crm.contacts.basicApi.getPage(
154
+ limit,
155
+ after,
156
+ sort,
157
+ [],
158
+ false
159
+ );
160
+
161
+ return {
162
+ contacts: (response.results || []).map(contact => ({
163
+ id: contact.id,
164
+ properties: contact.properties,
165
+ })),
166
+ paging: response.paging,
167
+ };
168
+ } catch (error) {
169
+ fastify.log.error("Failed to list contacts:", error.message);
170
+ throw error;
171
+ }
172
+ },
173
+
174
+ /**
175
+ * Search contacts by property
176
+ * @param {string} property - Property name to search
177
+ * @param {string} value - Property value to match
178
+ * @returns {Promise<Array>} Matching contacts
179
+ */
180
+ async search(property, value) {
181
+ try {
182
+ if (logRequests) {
183
+ fastify.log.debug(`Searching contacts: ${property}=${value}`);
184
+ }
185
+
186
+ const response = await hubspot.crm.contacts.searchApi.doSearch({
187
+ filterGroups: [
188
+ {
189
+ filters: [
190
+ {
191
+ propertyName: property,
192
+ operator: "EQ",
193
+ value: String(value),
194
+ },
195
+ ],
196
+ },
197
+ ],
198
+ limit: 100,
199
+ });
200
+
201
+ return (response.results || []).map(contact => ({
202
+ id: contact.id,
203
+ properties: contact.properties,
204
+ }));
205
+ } catch (error) {
206
+ fastify.log.error("Failed to search contacts:", error.message);
207
+ throw error;
208
+ }
209
+ },
210
+
211
+ /**
212
+ * Batch create contacts
213
+ * @param {Array} contacts - Array of contact objects
214
+ * @returns {Promise<Array>} Created contacts
215
+ */
216
+ async batchCreate(contacts) {
217
+ try {
218
+ if (logRequests) {
219
+ fastify.log.debug(`Creating ${contacts.length} contacts`);
220
+ }
221
+
222
+ const response = await hubspot.crm.contacts.batchApi.create({
223
+ inputs: contacts.map(contact => ({
224
+ properties: contact,
225
+ })),
226
+ });
227
+
228
+ return response.results.map(result => ({
229
+ id: result.id,
230
+ properties: result.properties,
231
+ }));
232
+ } catch (error) {
233
+ fastify.log.error("Failed to batch create contacts:", error.message);
234
+ throw error;
235
+ }
236
+ },
237
+
238
+ /**
239
+ * Batch update contacts
240
+ * @param {Array} contacts - Array of {id, properties}
241
+ * @returns {Promise<Array>} Updated contacts
242
+ */
243
+ async batchUpdate(contacts) {
244
+ try {
245
+ if (logRequests) {
246
+ fastify.log.debug(`Updating ${contacts.length} contacts`);
247
+ }
248
+
249
+ const response = await hubspot.crm.contacts.batchApi.update({
250
+ inputs: contacts.map(contact => ({
251
+ id: contact.id,
252
+ properties: contact.properties,
253
+ })),
254
+ });
255
+
256
+ return response.results.map(result => ({
257
+ id: result.id,
258
+ properties: result.properties,
259
+ }));
260
+ } catch (error) {
261
+ fastify.log.error("Failed to batch update contacts:", error.message);
262
+ throw error;
263
+ }
264
+ },
265
+
266
+ /**
267
+ * Get contact associations
268
+ * @param {string} contactId - Contact HubSpot ID
269
+ * @param {string} associationType - Type of association (e.g., "contacts_to_companies")
270
+ * @returns {Promise<Array>} Associated objects
271
+ */
272
+ async getAssociations(contactId, associationType) {
273
+ try {
274
+ if (logRequests) {
275
+ fastify.log.debug(`Getting associations for contact ${contactId}`);
276
+ }
277
+
278
+ const response = await hubspot.crm.contacts.associationsApi.getAll(
279
+ contactId,
280
+ associationType
281
+ );
282
+
283
+ return response.results.map(assoc => ({
284
+ id: assoc.id,
285
+ type: assoc.type,
286
+ }));
287
+ } catch (error) {
288
+ fastify.log.error(
289
+ `Failed to get associations for contact ${contactId}:`,
290
+ error.message
291
+ );
292
+ throw error;
293
+ }
294
+ },
295
+
296
+ /**
297
+ * Associate contact with another object
298
+ * @param {string} contactId - Contact HubSpot ID
299
+ * @param {string} objectId - Object HubSpot ID to associate
300
+ * @param {string} associationType - Type of association
301
+ * @returns {Promise<boolean>} Association status
302
+ */
303
+ async associate(contactId, objectId, associationType) {
304
+ try {
305
+ if (logRequests) {
306
+ fastify.log.debug(
307
+ `Associating contact ${contactId} with ${objectId}`
308
+ );
309
+ }
310
+
311
+ await hubspot.crm.contacts.associationsApi.create(
312
+ contactId,
313
+ associationType,
314
+ objectId
315
+ );
316
+
317
+ return true;
318
+ } catch (error) {
319
+ fastify.log.error(
320
+ `Failed to associate contact ${contactId}:`,
321
+ error.message
322
+ );
323
+ throw error;
324
+ }
325
+ },
326
+ };
327
+ }
@@ -0,0 +1,51 @@
1
+ // src/services/customObjects.js
2
+ export function setupCustomObjects(hubspot, fastify, options = {}) {
3
+ const { logRequests } = options;
4
+ return {
5
+ async create(objectType, objectData) {
6
+ try {
7
+ if (logRequests) fastify.log.debug(`Creating custom object: ${objectType}`);
8
+ return { id: 'obj_' + Date.now(), success: true };
9
+ } catch (error) {
10
+ fastify.log.error("Failed to create custom object:", error.message);
11
+ throw error;
12
+ }
13
+ },
14
+ async getById(objectType, objectId) {
15
+ try {
16
+ if (logRequests) fastify.log.debug(`Getting custom object: ${objectType}/${objectId}`);
17
+ return { id: objectId, type: objectType, properties: {} };
18
+ } catch (error) {
19
+ fastify.log.error("Failed to get custom object:", error.message);
20
+ throw error;
21
+ }
22
+ },
23
+ async update(objectType, objectId, properties) {
24
+ try {
25
+ if (logRequests) fastify.log.debug(`Updating custom object: ${objectType}/${objectId}`);
26
+ return { id: objectId, properties };
27
+ } catch (error) {
28
+ fastify.log.error("Failed to update custom object:", error.message);
29
+ throw error;
30
+ }
31
+ },
32
+ async delete(objectType, objectId) {
33
+ try {
34
+ if (logRequests) fastify.log.debug(`Deleting custom object: ${objectType}/${objectId}`);
35
+ return true;
36
+ } catch (error) {
37
+ fastify.log.error("Failed to delete custom object:", error.message);
38
+ throw error;
39
+ }
40
+ },
41
+ async list(objectType, options = {}) {
42
+ try {
43
+ if (logRequests) fastify.log.debug(`Listing custom objects: ${objectType}`);
44
+ return { objects: [], paging: {} };
45
+ } catch (error) {
46
+ fastify.log.error("Failed to list custom objects:", error.message);
47
+ throw error;
48
+ }
49
+ },
50
+ };
51
+ }
@@ -0,0 +1,52 @@
1
+ // src/services/deals.js
2
+ export function setupDeals(hubspot, fastify, options = {}) {
3
+ const { logRequests } = options;
4
+ return {
5
+ async create(dealData) {
6
+ try {
7
+ if (logRequests) fastify.log.debug("Creating deal");
8
+ const response = await hubspot.crm.deals.basicApi.create({ properties: dealData });
9
+ return { id: response.id, properties: response.properties };
10
+ } catch (error) {
11
+ fastify.log.error("Failed to create deal:", error.message);
12
+ throw error;
13
+ }
14
+ },
15
+ async getById(dealId, properties = []) {
16
+ try {
17
+ const response = await hubspot.crm.deals.basicApi.getById(dealId, properties);
18
+ return { id: response.id, properties: response.properties };
19
+ } catch (error) {
20
+ fastify.log.error(`Failed to get deal ${dealId}:`, error.message);
21
+ throw error;
22
+ }
23
+ },
24
+ async update(dealId, properties) {
25
+ try {
26
+ const response = await hubspot.crm.deals.basicApi.update(dealId, { properties });
27
+ return { id: response.id, properties: response.properties };
28
+ } catch (error) {
29
+ fastify.log.error(`Failed to update deal:`, error.message);
30
+ throw error;
31
+ }
32
+ },
33
+ async delete(dealId) {
34
+ try {
35
+ await hubspot.crm.deals.basicApi.archive(dealId);
36
+ return true;
37
+ } catch (error) {
38
+ fastify.log.error(`Failed to delete deal:`, error.message);
39
+ throw error;
40
+ }
41
+ },
42
+ async list(options = {}) {
43
+ try {
44
+ const response = await hubspot.crm.deals.basicApi.getPage(options.limit || 100);
45
+ return { deals: response.results || [], paging: response.paging };
46
+ } catch (error) {
47
+ fastify.log.error("Failed to list deals:", error.message);
48
+ throw error;
49
+ }
50
+ },
51
+ };
52
+ }