monday_ruby 1.1.0 → 1.2.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -1
  3. data/.rubocop.yml +2 -1
  4. data/CHANGELOG.md +14 -0
  5. data/CONTRIBUTING.md +104 -0
  6. data/README.md +146 -142
  7. data/docs/.vitepress/config.mjs +255 -0
  8. data/docs/.vitepress/theme/index.js +4 -0
  9. data/docs/.vitepress/theme/style.css +43 -0
  10. data/docs/README.md +80 -0
  11. data/docs/explanation/architecture.md +507 -0
  12. data/docs/explanation/best-practices/errors.md +478 -0
  13. data/docs/explanation/best-practices/performance.md +1084 -0
  14. data/docs/explanation/best-practices/rate-limiting.md +630 -0
  15. data/docs/explanation/best-practices/testing.md +820 -0
  16. data/docs/explanation/column-values.md +857 -0
  17. data/docs/explanation/design.md +795 -0
  18. data/docs/explanation/graphql.md +356 -0
  19. data/docs/explanation/migration/v1.md +808 -0
  20. data/docs/explanation/pagination.md +447 -0
  21. data/docs/guides/advanced/batch.md +1274 -0
  22. data/docs/guides/advanced/complex-queries.md +1114 -0
  23. data/docs/guides/advanced/errors.md +818 -0
  24. data/docs/guides/advanced/pagination.md +934 -0
  25. data/docs/guides/advanced/rate-limiting.md +981 -0
  26. data/docs/guides/authentication.md +286 -0
  27. data/docs/guides/boards/create.md +386 -0
  28. data/docs/guides/boards/delete.md +405 -0
  29. data/docs/guides/boards/duplicate.md +511 -0
  30. data/docs/guides/boards/query.md +530 -0
  31. data/docs/guides/boards/update.md +453 -0
  32. data/docs/guides/columns/create.md +452 -0
  33. data/docs/guides/columns/metadata.md +492 -0
  34. data/docs/guides/columns/query.md +455 -0
  35. data/docs/guides/columns/update-multiple.md +459 -0
  36. data/docs/guides/columns/update-values.md +509 -0
  37. data/docs/guides/files/add-to-column.md +40 -0
  38. data/docs/guides/files/add-to-update.md +37 -0
  39. data/docs/guides/files/clear-column.md +33 -0
  40. data/docs/guides/first-request.md +285 -0
  41. data/docs/guides/folders/manage.md +750 -0
  42. data/docs/guides/groups/items.md +626 -0
  43. data/docs/guides/groups/manage.md +501 -0
  44. data/docs/guides/installation.md +169 -0
  45. data/docs/guides/items/create.md +493 -0
  46. data/docs/guides/items/delete.md +514 -0
  47. data/docs/guides/items/query.md +605 -0
  48. data/docs/guides/items/subitems.md +483 -0
  49. data/docs/guides/items/update.md +699 -0
  50. data/docs/guides/updates/manage.md +619 -0
  51. data/docs/guides/use-cases/dashboard.md +1421 -0
  52. data/docs/guides/use-cases/import.md +1962 -0
  53. data/docs/guides/use-cases/task-management.md +1381 -0
  54. data/docs/guides/workspaces/manage.md +502 -0
  55. data/docs/index.md +69 -0
  56. data/docs/package-lock.json +2468 -0
  57. data/docs/package.json +13 -0
  58. data/docs/reference/client.md +540 -0
  59. data/docs/reference/configuration.md +586 -0
  60. data/docs/reference/errors.md +693 -0
  61. data/docs/reference/resources/account.md +208 -0
  62. data/docs/reference/resources/activity-log.md +369 -0
  63. data/docs/reference/resources/board-view.md +359 -0
  64. data/docs/reference/resources/board.md +393 -0
  65. data/docs/reference/resources/column.md +543 -0
  66. data/docs/reference/resources/file.md +236 -0
  67. data/docs/reference/resources/folder.md +386 -0
  68. data/docs/reference/resources/group.md +507 -0
  69. data/docs/reference/resources/item.md +348 -0
  70. data/docs/reference/resources/subitem.md +267 -0
  71. data/docs/reference/resources/update.md +259 -0
  72. data/docs/reference/resources/workspace.md +213 -0
  73. data/docs/reference/response.md +560 -0
  74. data/docs/tutorial/first-integration.md +713 -0
  75. data/lib/monday/client.rb +24 -0
  76. data/lib/monday/configuration.rb +5 -0
  77. data/lib/monday/request.rb +15 -0
  78. data/lib/monday/resources/base.rb +4 -0
  79. data/lib/monday/resources/file.rb +56 -0
  80. data/lib/monday/util.rb +1 -0
  81. data/lib/monday/version.rb +1 -1
  82. metadata +87 -4
@@ -0,0 +1,818 @@
1
+ # Error Handling
2
+
3
+ Learn how to handle errors when using the monday_ruby gem, from basic rescue blocks to advanced retry strategies.
4
+
5
+ ## Understanding monday_ruby Errors
6
+
7
+ The monday_ruby gem provides a comprehensive error hierarchy that maps both HTTP status codes and monday.com API error codes to specific Ruby exception classes.
8
+
9
+ ### Error Hierarchy
10
+
11
+ All errors inherit from `Monday::Error`, making it easy to catch any monday.com-related error:
12
+
13
+ ```ruby
14
+ Monday::Error (base class)
15
+ ├── Monday::AuthorizationError (401, 403)
16
+ ├── Monday::InvalidRequestError (400)
17
+ ├── Monday::ResourceNotFoundError (404)
18
+ ├── Monday::InternalServerError (500)
19
+ ├── Monday::RateLimitError (429)
20
+ └── Monday::ComplexityError (GraphQL complexity limit)
21
+ ```
22
+
23
+ ### When Errors Are Raised
24
+
25
+ Errors are raised in two scenarios:
26
+
27
+ 1. **HTTP Status Codes**: Non-2xx status codes (401, 404, 500, etc.)
28
+ 2. **GraphQL Error Codes**: Even when HTTP status is 200, GraphQL errors in the response trigger exceptions
29
+
30
+ ```ruby
31
+ # Example: HTTP 401 raises Monday::AuthorizationError
32
+ client = Monday::Client.new(token: "invalid_token")
33
+ client.account.query # => Monday::AuthorizationError
34
+
35
+ # Example: HTTP 200 with error_code raises Monday::InvalidRequestError
36
+ client.board.query(args: {ids: [999999]}) # => Monday::InvalidRequestError
37
+ ```
38
+
39
+ ### Error Properties
40
+
41
+ Every error object provides access to:
42
+
43
+ ```ruby
44
+ begin
45
+ client.board.query(args: {ids: [123]})
46
+ rescue Monday::Error => e
47
+ e.message # Human-readable error message
48
+ e.code # Error code (HTTP status or error_code)
49
+ e.response # Full Monday::Response object
50
+ e.error_data # Additional error metadata (hash)
51
+ end
52
+ ```
53
+
54
+ ## Basic Error Handling
55
+
56
+ ### Check Response Success
57
+
58
+ The safest approach is to check `response.success?` before accessing data:
59
+
60
+ ```ruby
61
+ response = client.board.query(args: {ids: [123]}, select: ["id", "name"])
62
+
63
+ if response.success?
64
+ boards = response.body["data"]["boards"]
65
+ puts "Found #{boards.length} boards"
66
+ else
67
+ puts "Request failed: #{response.body["error_message"]}"
68
+ end
69
+ ```
70
+
71
+ ### Rescue Specific Errors
72
+
73
+ Catch specific error types to handle different scenarios:
74
+
75
+ ```ruby
76
+ begin
77
+ response = client.item.create(
78
+ args: {
79
+ board_id: 123,
80
+ item_name: "New Task"
81
+ },
82
+ select: ["id", "name"]
83
+ )
84
+
85
+ item = response.body["data"]["create_item"]
86
+ puts "Created item: #{item["name"]}"
87
+
88
+ rescue Monday::AuthorizationError => e
89
+ puts "Authentication failed. Check your API token."
90
+
91
+ rescue Monday::InvalidRequestError => e
92
+ puts "Invalid request: #{e.message}"
93
+ # Check error_data for specifics
94
+ puts "Error details: #{e.error_data}"
95
+
96
+ rescue Monday::Error => e
97
+ puts "Unexpected error: #{e.message}"
98
+ end
99
+ ```
100
+
101
+ ### Access Error Messages
102
+
103
+ Extract error information from the exception:
104
+
105
+ ```ruby
106
+ begin
107
+ client.folder.delete(args: {folder_id: "invalid_id"})
108
+ rescue Monday::Error => e
109
+ puts "Error code: #{e.code}"
110
+ puts "Error message: #{e.message}"
111
+
112
+ # Access the raw response
113
+ puts "HTTP status: #{e.response.status}"
114
+ puts "Response body: #{e.response.body}"
115
+
116
+ # Get additional error data
117
+ if e.error_data.any?
118
+ puts "Error data: #{e.error_data.inspect}"
119
+ end
120
+ end
121
+ ```
122
+
123
+ ## Handle Common Errors
124
+
125
+ ### AuthorizationError (401, 403)
126
+
127
+ Raised when authentication fails or you lack permissions.
128
+
129
+ **Common causes:**
130
+ - Invalid API token
131
+ - Token doesn't have required permissions
132
+ - Token has been revoked
133
+
134
+ ```ruby
135
+ begin
136
+ client = Monday::Client.new(token: ENV["MONDAY_TOKEN"])
137
+ response = client.account.query(select: ["id", "name"])
138
+
139
+ rescue Monday::AuthorizationError => e
140
+ puts "Authorization failed: #{e.message}"
141
+ puts "Please check your API token in the monday.com Developer Portal"
142
+ puts "Make sure the token has the required scopes"
143
+
144
+ # Log for debugging
145
+ logger.error("Monday.com auth error: #{e.message}")
146
+
147
+ # Return nil or default value
148
+ nil
149
+ end
150
+ ```
151
+
152
+ **Real error example:**
153
+ ```json
154
+ {
155
+ "errors": ["Not Authenticated"],
156
+ "status": 401
157
+ }
158
+ ```
159
+
160
+ ### InvalidRequestError (400)
161
+
162
+ Raised when request parameters are invalid.
163
+
164
+ **Common causes:**
165
+ - Invalid board/item/column IDs
166
+ - Malformed GraphQL query
167
+ - Invalid column values
168
+ - Missing required parameters
169
+
170
+ ```ruby
171
+ begin
172
+ response = client.column.change_simple_value(
173
+ args: {
174
+ board_id: 123, # Invalid board ID
175
+ item_id: 456,
176
+ column_id: "status",
177
+ value: "Working on it"
178
+ },
179
+ select: ["id", "name"]
180
+ )
181
+
182
+ rescue Monday::InvalidRequestError => e
183
+ case e.error_data
184
+ when ->(data) { data["board_id"] }
185
+ puts "Board not found: #{e.error_data["board_id"]}"
186
+ puts "Please verify the board ID and try again"
187
+
188
+ when ->(data) { data["item_id"] }
189
+ puts "Item not found: #{e.error_data["item_id"]}"
190
+
191
+ when ->(data) { data["column_id"] }
192
+ puts "Invalid column: #{e.error_data["column_id"]}"
193
+
194
+ else
195
+ puts "Invalid request: #{e.message}"
196
+ end
197
+ end
198
+ ```
199
+
200
+ **Real error example:**
201
+ ```json
202
+ {
203
+ "error_message": "The board does not exist. Please check your board ID and try again",
204
+ "error_code": "InvalidBoardIdException",
205
+ "error_data": {"board_id": 123},
206
+ "status_code": 200
207
+ }
208
+ ```
209
+
210
+ ### ResourceNotFoundError (404)
211
+
212
+ Raised when a resource doesn't exist.
213
+
214
+ ```ruby
215
+ def delete_folder_safely(client, folder_id)
216
+ begin
217
+ response = client.folder.delete(args: {folder_id: folder_id})
218
+ puts "Folder deleted successfully"
219
+
220
+ rescue Monday::ResourceNotFoundError => e
221
+ puts "Folder not found: #{e.error_data["folder_id"]}"
222
+ puts "It may have already been deleted"
223
+ # Don't raise - this is acceptable
224
+
225
+ rescue Monday::Error => e
226
+ puts "Failed to delete folder: #{e.message}"
227
+ raise # Re-raise for unexpected errors
228
+ end
229
+ end
230
+ ```
231
+
232
+ **Real error example:**
233
+ ```json
234
+ {
235
+ "error_message": "The folder does not exist. Please check your folder ID and try again",
236
+ "error_code": "InvalidFolderIdException",
237
+ "error_data": {"folder_id": 0},
238
+ "status_code": 200
239
+ }
240
+ ```
241
+
242
+ ### RateLimitError (429)
243
+
244
+ Raised when you exceed monday.com's rate limits.
245
+
246
+ **Rate limits:**
247
+ - Queries: Complexity-based (max 10,000,000 per minute)
248
+ - Mutations: 60 requests per minute per user
249
+
250
+ ```ruby
251
+ def query_with_rate_limit_handling(client)
252
+ begin
253
+ response = client.board.query(
254
+ args: {limit: 100},
255
+ select: ["id", "name", "items"]
256
+ )
257
+
258
+ rescue Monday::RateLimitError => e
259
+ puts "Rate limit exceeded: #{e.message}"
260
+
261
+ # Wait before retrying
262
+ sleep 60
263
+
264
+ puts "Retrying after rate limit cooldown..."
265
+ retry
266
+ end
267
+ end
268
+ ```
269
+
270
+ ### InternalServerError (500)
271
+
272
+ Raised when monday.com's servers encounter an error.
273
+
274
+ **Common causes:**
275
+ - monday.com service issues
276
+ - Invalid item/board ID causing server error
277
+ - Temporary API outages
278
+
279
+ ```ruby
280
+ def create_update_with_server_error_handling(client, item_id, body)
281
+ max_retries = 3
282
+ retry_count = 0
283
+
284
+ begin
285
+ response = client.update.create(
286
+ args: {item_id: item_id, body: body},
287
+ select: ["id", "body", "created_at"]
288
+ )
289
+
290
+ response.body["data"]["create_update"]
291
+
292
+ rescue Monday::InternalServerError => e
293
+ retry_count += 1
294
+
295
+ if retry_count < max_retries
296
+ puts "Server error (attempt #{retry_count}/#{max_retries}): #{e.message}"
297
+ sleep 2 ** retry_count # Exponential backoff
298
+ retry
299
+ else
300
+ puts "Server error persists after #{max_retries} attempts"
301
+ raise
302
+ end
303
+ end
304
+ end
305
+ ```
306
+
307
+ **Real error example:**
308
+ ```json
309
+ {
310
+ "status_code": 500,
311
+ "error_message": "Internal server error",
312
+ "error_code": "INTERNAL_SERVER_ERROR"
313
+ }
314
+ ```
315
+
316
+ ## Advanced Error Handling
317
+
318
+ ### Retry Logic with Exponential Backoff
319
+
320
+ Implement smart retry logic for transient errors:
321
+
322
+ ```ruby
323
+ class MondayRetryHandler
324
+ MAX_RETRIES = 3
325
+ BASE_DELAY = 1 # seconds
326
+
327
+ def self.with_retry(&block)
328
+ retry_count = 0
329
+
330
+ begin
331
+ yield
332
+
333
+ rescue Monday::RateLimitError => e
334
+ # Always wait for rate limits
335
+ puts "Rate limited. Waiting 60 seconds..."
336
+ sleep 60
337
+ retry
338
+
339
+ rescue Monday::InternalServerError, Monday::Error => e
340
+ retry_count += 1
341
+
342
+ if retry_count < MAX_RETRIES
343
+ delay = BASE_DELAY * (2 ** (retry_count - 1))
344
+ puts "Error: #{e.message}. Retrying in #{delay}s (attempt #{retry_count}/#{MAX_RETRIES})"
345
+ sleep delay
346
+ retry
347
+ else
348
+ puts "Failed after #{MAX_RETRIES} attempts"
349
+ raise
350
+ end
351
+ end
352
+ end
353
+ end
354
+
355
+ # Usage
356
+ MondayRetryHandler.with_retry do
357
+ client.item.create(
358
+ args: {board_id: 123, item_name: "New Task"},
359
+ select: ["id", "name"]
360
+ )
361
+ end
362
+ ```
363
+
364
+ ### Rescue with Fallbacks
365
+
366
+ Provide fallback values when errors occur:
367
+
368
+ ```ruby
369
+ def get_board_or_default(client, board_id)
370
+ response = client.board.query(
371
+ args: {ids: [board_id]},
372
+ select: ["id", "name", "description"]
373
+ )
374
+
375
+ response.body["data"]["boards"].first
376
+
377
+ rescue Monday::ResourceNotFoundError
378
+ # Return a default board structure
379
+ {
380
+ "id" => nil,
381
+ "name" => "Board not found",
382
+ "description" => ""
383
+ }
384
+
385
+ rescue Monday::AuthorizationError
386
+ # Return nil for auth errors
387
+ nil
388
+
389
+ rescue Monday::Error => e
390
+ # Log unexpected errors
391
+ puts "Unexpected error: #{e.message}"
392
+ nil
393
+ end
394
+ ```
395
+
396
+ ### Error Logging
397
+
398
+ Integrate with your logging system:
399
+
400
+ ```ruby
401
+ require 'logger'
402
+
403
+ class MondayClient
404
+ def initialize(token:, logger: Logger.new(STDOUT))
405
+ @client = Monday::Client.new(token: token)
406
+ @logger = logger
407
+ end
408
+
409
+ def safe_query(resource, method, args)
410
+ response = @client.public_send(resource).public_send(method, **args)
411
+
412
+ if response.success?
413
+ @logger.info("monday.com API success: #{resource}.#{method}")
414
+ response.body["data"]
415
+ else
416
+ @logger.error("monday.com API error: #{response.body["error_message"]}")
417
+ nil
418
+ end
419
+
420
+ rescue Monday::AuthorizationError => e
421
+ @logger.error("monday.com auth error: #{e.message}")
422
+ raise
423
+
424
+ rescue Monday::RateLimitError => e
425
+ @logger.warn("monday.com rate limit: #{e.message}")
426
+ raise
427
+
428
+ rescue Monday::Error => e
429
+ @logger.error("monday.com error: #{e.class} - #{e.message}")
430
+ @logger.error("Error data: #{e.error_data.inspect}") if e.error_data.any?
431
+ raise
432
+ end
433
+ end
434
+
435
+ # Usage
436
+ client = MondayClient.new(
437
+ token: ENV["MONDAY_TOKEN"],
438
+ logger: Logger.new("monday.log")
439
+ )
440
+
441
+ client.safe_query(:board, :query, {args: {ids: [123]}, select: ["id", "name"]})
442
+ ```
443
+
444
+ ### Validation Before API Calls
445
+
446
+ Prevent errors by validating input:
447
+
448
+ ```ruby
449
+ module MondayValidation
450
+ class ValidationError < StandardError; end
451
+
452
+ def self.validate_board_id!(board_id)
453
+ raise ValidationError, "board_id must be an integer" unless board_id.is_a?(Integer)
454
+ raise ValidationError, "board_id must be positive" unless board_id > 0
455
+ end
456
+
457
+ def self.validate_item_name!(item_name)
458
+ raise ValidationError, "item_name cannot be empty" if item_name.to_s.strip.empty?
459
+ raise ValidationError, "item_name too long (max 255 chars)" if item_name.length > 255
460
+ end
461
+
462
+ def self.validate_column_value!(column_id, value)
463
+ raise ValidationError, "column_id cannot be empty" if column_id.to_s.strip.empty?
464
+ raise ValidationError, "value cannot be nil" if value.nil?
465
+ end
466
+ end
467
+
468
+ # Usage
469
+ def create_item_safely(client, board_id, item_name)
470
+ # Validate before making API call
471
+ MondayValidation.validate_board_id!(board_id)
472
+ MondayValidation.validate_item_name!(item_name)
473
+
474
+ client.item.create(
475
+ args: {board_id: board_id, item_name: item_name},
476
+ select: ["id", "name"]
477
+ )
478
+
479
+ rescue MondayValidation::ValidationError => e
480
+ puts "Validation error: #{e.message}"
481
+ nil
482
+
483
+ rescue Monday::Error => e
484
+ puts "API error: #{e.message}"
485
+ nil
486
+ end
487
+ ```
488
+
489
+ ## Error Handling Patterns
490
+
491
+ ### Safe Wrapper for API Calls
492
+
493
+ Create a reusable wrapper for consistent error handling:
494
+
495
+ ```ruby
496
+ class SafeMondayClient
497
+ def initialize(client)
498
+ @client = client
499
+ end
500
+
501
+ def safe_call(resource, method, args = {})
502
+ response = @client.public_send(resource).public_send(method, **args)
503
+
504
+ yield(response.body["data"]) if block_given? && response.success?
505
+
506
+ response.success? ? response.body["data"] : nil
507
+
508
+ rescue Monday::AuthorizationError => e
509
+ handle_auth_error(e)
510
+ nil
511
+
512
+ rescue Monday::ResourceNotFoundError => e
513
+ handle_not_found_error(e)
514
+ nil
515
+
516
+ rescue Monday::RateLimitError => e
517
+ handle_rate_limit_error(e)
518
+ sleep 60
519
+ retry
520
+
521
+ rescue Monday::Error => e
522
+ handle_generic_error(e)
523
+ nil
524
+ end
525
+
526
+ private
527
+
528
+ def handle_auth_error(error)
529
+ puts "Authentication failed. Please check your API token."
530
+ # Send alert, log to monitoring service, etc.
531
+ end
532
+
533
+ def handle_not_found_error(error)
534
+ puts "Resource not found: #{error.message}"
535
+ end
536
+
537
+ def handle_rate_limit_error(error)
538
+ puts "Rate limit exceeded. Retrying in 60 seconds..."
539
+ end
540
+
541
+ def handle_generic_error(error)
542
+ puts "Error: #{error.message}"
543
+ # Log to error tracking service (Sentry, Rollbar, etc.)
544
+ end
545
+ end
546
+
547
+ # Usage
548
+ client = Monday::Client.new(token: ENV["MONDAY_TOKEN"])
549
+ safe_client = SafeMondayClient.new(client)
550
+
551
+ data = safe_client.safe_call(:board, :query, {
552
+ args: {ids: [123]},
553
+ select: ["id", "name"]
554
+ })
555
+
556
+ if data
557
+ boards = data["boards"]
558
+ puts "Found #{boards.length} boards"
559
+ else
560
+ puts "Failed to retrieve boards"
561
+ end
562
+ ```
563
+
564
+ ### Graceful Degradation
565
+
566
+ Continue operation even when some calls fail:
567
+
568
+ ```ruby
569
+ def get_dashboard_data(client)
570
+ dashboard = {
571
+ boards: [],
572
+ workspaces: [],
573
+ account: nil
574
+ }
575
+
576
+ # Try to get boards
577
+ begin
578
+ response = client.board.query(args: {limit: 10}, select: ["id", "name"])
579
+ dashboard[:boards] = response.body["data"]["boards"] if response.success?
580
+ rescue Monday::Error => e
581
+ puts "Failed to load boards: #{e.message}"
582
+ # Continue anyway - boards will be empty array
583
+ end
584
+
585
+ # Try to get workspaces
586
+ begin
587
+ response = client.workspace.query(select: ["id", "name"])
588
+ dashboard[:workspaces] = response.body["data"]["workspaces"] if response.success?
589
+ rescue Monday::Error => e
590
+ puts "Failed to load workspaces: #{e.message}"
591
+ # Continue anyway
592
+ end
593
+
594
+ # Try to get account
595
+ begin
596
+ response = client.account.query(select: ["id", "name"])
597
+ dashboard[:account] = response.body["data"]["account"] if response.success?
598
+ rescue Monday::Error => e
599
+ puts "Failed to load account: #{e.message}"
600
+ # Continue anyway
601
+ end
602
+
603
+ # Return partial data - better than nothing
604
+ dashboard
605
+ end
606
+
607
+ # Usage
608
+ dashboard = get_dashboard_data(client)
609
+ puts "Loaded #{dashboard[:boards].length} boards"
610
+ puts "Loaded #{dashboard[:workspaces].length} workspaces"
611
+ puts dashboard[:account] ? "Account: #{dashboard[:account]["name"]}" : "Account unavailable"
612
+ ```
613
+
614
+ ### User-Friendly Error Messages
615
+
616
+ Convert technical errors to user-friendly messages:
617
+
618
+ ```ruby
619
+ module MondayErrorMessages
620
+ def self.humanize(error)
621
+ case error
622
+ when Monday::AuthorizationError
623
+ "Unable to connect to monday.com. Please check your access token."
624
+
625
+ when Monday::ResourceNotFoundError
626
+ if error.error_data["board_id"]
627
+ "The board you're looking for doesn't exist or has been deleted."
628
+ elsif error.error_data["item_id"]
629
+ "The item you're looking for doesn't exist or has been deleted."
630
+ elsif error.error_data["folder_id"]
631
+ "The folder you're looking for doesn't exist or has been deleted."
632
+ else
633
+ "The resource you're looking for could not be found."
634
+ end
635
+
636
+ when Monday::RateLimitError
637
+ "You're making too many requests. Please wait a minute and try again."
638
+
639
+ when Monday::InternalServerError
640
+ "monday.com is experiencing technical difficulties. Please try again later."
641
+
642
+ when Monday::InvalidRequestError
643
+ case error.message
644
+ when /InvalidBoardIdException/
645
+ "Invalid board. Please check the board ID and try again."
646
+ when /InvalidItemIdException/
647
+ "Invalid item. Please check the item ID and try again."
648
+ when /InvalidColumnIdException/
649
+ "Invalid column. Please check the column ID and try again."
650
+ when /ColumnValueException/
651
+ "Invalid column value. Please check the format and try again."
652
+ else
653
+ "Invalid request. Please check your input and try again."
654
+ end
655
+
656
+ else
657
+ "An unexpected error occurred. Please try again or contact support."
658
+ end
659
+ end
660
+ end
661
+
662
+ # Usage in a web application
663
+ begin
664
+ response = client.item.create(
665
+ args: {board_id: params[:board_id], item_name: params[:item_name]},
666
+ select: ["id", "name"]
667
+ )
668
+
669
+ flash[:success] = "Item created successfully!"
670
+ redirect_to board_path(params[:board_id])
671
+
672
+ rescue Monday::Error => e
673
+ flash[:error] = MondayErrorMessages.humanize(e)
674
+ redirect_to :back
675
+ end
676
+ ```
677
+
678
+ ## GraphQL Errors
679
+
680
+ ### Access GraphQL Error Details
681
+
682
+ monday.com returns GraphQL errors with detailed information:
683
+
684
+ ```ruby
685
+ begin
686
+ response = client.account.query(select: ["id", "invalid_field"])
687
+ rescue Monday::Error => e
688
+ # Access the full error array
689
+ if e.response.body["errors"]
690
+ e.response.body["errors"].each do |error|
691
+ puts "Error: #{error["message"]}"
692
+ puts "Location: line #{error["locations"]&.first&.dig("line")}"
693
+ puts "Path: #{error["path"]&.join(" > ")}"
694
+ puts "Code: #{error.dig("extensions", "code")}"
695
+ end
696
+ end
697
+ end
698
+ ```
699
+
700
+ **Real GraphQL error structure:**
701
+ ```json
702
+ {
703
+ "errors": [
704
+ {
705
+ "message": "Field 'invalid_field' doesn't exist on type 'Account'",
706
+ "locations": [{"line": 1, "column": 10}],
707
+ "path": ["account"],
708
+ "extensions": {
709
+ "code": "undefinedField",
710
+ "typeName": "Account",
711
+ "fieldName": "invalid_field"
712
+ }
713
+ }
714
+ ]
715
+ }
716
+ ```
717
+
718
+ ### Parse GraphQL Error Messages
719
+
720
+ Extract meaningful information from GraphQL errors:
721
+
722
+ ```ruby
723
+ def parse_graphql_errors(response_body)
724
+ return [] unless response_body["errors"]
725
+
726
+ response_body["errors"].map do |error|
727
+ {
728
+ message: error["message"],
729
+ field: error.dig("extensions", "fieldName"),
730
+ type: error.dig("extensions", "typeName"),
731
+ code: error.dig("extensions", "code"),
732
+ path: error["path"]&.join("."),
733
+ line: error.dig("locations", 0, "line")
734
+ }
735
+ end
736
+ end
737
+
738
+ # Usage
739
+ begin
740
+ response = client.board.query(
741
+ args: {ids: [123]},
742
+ select: ["id", "invalid_field"]
743
+ )
744
+ rescue Monday::Error => e
745
+ errors = parse_graphql_errors(e.response.body)
746
+
747
+ errors.each do |error|
748
+ puts "GraphQL Error:"
749
+ puts " Message: #{error[:message]}"
750
+ puts " Field: #{error[:field]}" if error[:field]
751
+ puts " Type: #{error[:type]}" if error[:type]
752
+ puts " Code: #{error[:code]}" if error[:code]
753
+ end
754
+ end
755
+ ```
756
+
757
+ ### Handle Field-Specific Errors
758
+
759
+ Catch errors for specific fields:
760
+
761
+ ```ruby
762
+ def query_board_with_fallback_fields(client, board_id)
763
+ # Try querying with all desired fields
764
+ fields = ["id", "name", "description", "items_count", "board_kind"]
765
+
766
+ begin
767
+ response = client.board.query(
768
+ args: {ids: [board_id]},
769
+ select: fields
770
+ )
771
+
772
+ return response.body["data"]["boards"].first if response.success?
773
+
774
+ rescue Monday::Error => e
775
+ if e.response.body["errors"]
776
+ # Find which fields are invalid
777
+ invalid_fields = e.response.body["errors"].map do |error|
778
+ error.dig("extensions", "fieldName")
779
+ end.compact
780
+
781
+ # Retry with only valid fields
782
+ valid_fields = fields - invalid_fields
783
+
784
+ if valid_fields.any?
785
+ puts "Retrying with valid fields: #{valid_fields.join(", ")}"
786
+
787
+ response = client.board.query(
788
+ args: {ids: [board_id]},
789
+ select: valid_fields
790
+ )
791
+
792
+ return response.body["data"]["boards"].first if response.success?
793
+ end
794
+ end
795
+
796
+ raise # Re-raise if we can't recover
797
+ end
798
+ end
799
+ ```
800
+
801
+ ## Best Practices
802
+
803
+ 1. **Always handle errors**: Never assume API calls will succeed
804
+ 2. **Be specific**: Rescue specific error classes rather than catching all errors
805
+ 3. **Validate input**: Check parameters before making API calls
806
+ 4. **Log errors**: Keep track of errors for debugging and monitoring
807
+ 5. **Retry wisely**: Implement exponential backoff for transient errors
808
+ 6. **Fail gracefully**: Provide fallback values or partial data when possible
809
+ 7. **User-friendly messages**: Convert technical errors to readable messages
810
+ 8. **Monitor rate limits**: Track your API usage to avoid rate limit errors
811
+ 9. **Check error_data**: Use the error_data hash for context-specific handling
812
+ 10. **Test error paths**: Write tests for your error handling code
813
+
814
+ ## Related Resources
815
+
816
+ - [monday.com API Rate Limits](https://developer.monday.com/api-reference/docs/rate-limits)
817
+ - [GraphQL Error Handling](https://developer.monday.com/api-reference/docs/errors)
818
+ - [Authentication Guide](../authentication.md)