rails_react_errors 0.1.0 → 0.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.
- checksums.yaml +4 -4
- data/README.md +148 -35
- data/lib/rails_react_errors/configuration.rb +3 -1
- data/lib/rails_react_errors/controller.rb +34 -6
- data/lib/rails_react_errors/renderer.rb +12 -0
- data/lib/rails_react_errors/serializer.rb +39 -0
- data/lib/rails_react_errors/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac6abfaf6c7bcfb5c7b9e363ecf4385e313043b48b9a6c488f1d618a8dcab77d
|
|
4
|
+
data.tar.gz: 4af4ffa98d1d3b4e9d64064e5a10f10f28ffc86ccfd10be4aeb61c309b37ae2f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e73812697af09fdc076ae881e55dfab37969d1a90e6b75d59546e08fa3b34e4d4183dcde978a1452cdc6b1b621b74dabf666cb28d1034a758eb62a95e2d9d78
|
|
7
|
+
data.tar.gz: dd7cd735f7cc112029b4d5434dfbe8a0d2a2f0fc03bf06bc09144d6d72ad187d730ddfd2fbafe0e140276971c05ca9d2847e0bdf45634d608f71680e1073b0b4
|
data/README.md
CHANGED
|
@@ -1,74 +1,187 @@
|
|
|
1
|
-
#
|
|
1
|
+
# rails_react_errors
|
|
2
|
+
|
|
3
|
+
Consistent error responses for **Rails APIs used by React frontends**.
|
|
4
|
+
|
|
5
|
+
`rails_react_errors` standardizes API error responses so React applications can easily handle validation errors, authentication errors, and server failures.
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
Instead of handling multiple Rails error formats, this gem provides a **single predictable JSON structure**.
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
---
|
|
6
10
|
|
|
7
11
|
## Installation
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
Add this line to your application's Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem "rails_react_errors"
|
|
17
|
+
```
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
Then run:
|
|
12
20
|
|
|
13
21
|
```bash
|
|
14
|
-
bundle
|
|
22
|
+
bundle install
|
|
15
23
|
```
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
---
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
## Quick Setup
|
|
28
|
+
|
|
29
|
+
Include the controller module in your base controller.
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
class ApplicationController < ActionController::API
|
|
33
|
+
include RailsReactErrors::Controller
|
|
34
|
+
end
|
|
21
35
|
```
|
|
22
36
|
|
|
23
|
-
|
|
37
|
+
That's it. Your Rails API will now automatically return standardized error responses.
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
---
|
|
26
40
|
|
|
27
|
-
##
|
|
41
|
+
## Automatic Exception Handling
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
The gem automatically handles common Rails exceptions and converts them into consistent JSON responses.
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
Supported exceptions:
|
|
32
46
|
|
|
33
|
-
|
|
47
|
+
* `ActiveRecord::RecordNotFound`
|
|
48
|
+
* `ActiveRecord::RecordInvalid`
|
|
49
|
+
* `ActiveRecord::RecordNotUnique`
|
|
50
|
+
* `ActionController::ParameterMissing`
|
|
51
|
+
* `JSON::ParserError`
|
|
52
|
+
* `StandardError` *(optional)*
|
|
34
53
|
|
|
35
|
-
|
|
54
|
+
Example:
|
|
36
55
|
|
|
37
|
-
|
|
56
|
+
```ruby
|
|
57
|
+
class UsersController < ApplicationController
|
|
58
|
+
def show
|
|
59
|
+
user = User.find(params[:id])
|
|
60
|
+
render json: user
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
```
|
|
38
64
|
|
|
39
|
-
|
|
65
|
+
If the record does not exist:
|
|
40
66
|
|
|
41
|
-
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"success": false,
|
|
70
|
+
"message": "Couldn't find User with 'id'=99",
|
|
71
|
+
"code": "NOT_FOUND",
|
|
72
|
+
"errors": {}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
42
75
|
|
|
43
|
-
|
|
76
|
+
---
|
|
44
77
|
|
|
45
|
-
|
|
78
|
+
## Validation Error Example
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
```ruby
|
|
81
|
+
class UsersController < ApplicationController
|
|
82
|
+
def create
|
|
83
|
+
user = User.create!(user_params)
|
|
84
|
+
render json: { success: true, data: user }, status: :created
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def user_params
|
|
90
|
+
params.require(:user).permit(:email, :password)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
```
|
|
48
94
|
|
|
49
|
-
|
|
95
|
+
Response:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"success": false,
|
|
100
|
+
"message": "Validation failed",
|
|
101
|
+
"code": "VALIDATION_ERROR",
|
|
102
|
+
"errors": {
|
|
103
|
+
"email": ["Email can't be blank"]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
50
107
|
|
|
51
|
-
|
|
108
|
+
---
|
|
52
109
|
|
|
53
|
-
##
|
|
110
|
+
## JSON Parsing Error
|
|
54
111
|
|
|
55
|
-
|
|
112
|
+
If an invalid JSON payload is sent:
|
|
56
113
|
|
|
57
|
-
|
|
114
|
+
```json
|
|
115
|
+
{ email: "test"
|
|
116
|
+
```
|
|
58
117
|
|
|
59
|
-
|
|
118
|
+
Response:
|
|
60
119
|
|
|
61
|
-
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"success": false,
|
|
123
|
+
"message": "Invalid JSON payload",
|
|
124
|
+
"code": "INVALID_JSON",
|
|
125
|
+
"errors": {}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
62
128
|
|
|
63
|
-
|
|
129
|
+
---
|
|
64
130
|
|
|
65
|
-
|
|
131
|
+
## Available Helper Methods
|
|
66
132
|
|
|
67
|
-
|
|
133
|
+
You can also manually render errors if needed.
|
|
68
134
|
|
|
69
|
-
|
|
135
|
+
```ruby
|
|
136
|
+
render_validation_error(record)
|
|
137
|
+
render_not_found_error("User not found")
|
|
138
|
+
render_parameter_missing_error("param is missing")
|
|
139
|
+
render_conflict_error("Duplicate record")
|
|
140
|
+
render_server_error("Something went wrong")
|
|
141
|
+
render_error(message:, code:, status:, errors: {})
|
|
142
|
+
```
|
|
70
143
|
|
|
71
|
-
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Custom Exception Mapping
|
|
147
|
+
|
|
148
|
+
You can configure your own exceptions.
|
|
72
149
|
|
|
73
150
|
```ruby
|
|
74
|
-
|
|
151
|
+
RailsReactErrors.configure do |config|
|
|
152
|
+
config.custom_exceptions = {
|
|
153
|
+
"Pundit::NotAuthorizedError" => {
|
|
154
|
+
code: "FORBIDDEN",
|
|
155
|
+
status: :forbidden
|
|
156
|
+
},
|
|
157
|
+
"JWT::DecodeError" => {
|
|
158
|
+
code: "INVALID_TOKEN",
|
|
159
|
+
status: :unauthorized
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Enable Global Error Handling
|
|
168
|
+
|
|
169
|
+
To automatically handle unexpected errors:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
RailsReactErrors.configure do |config|
|
|
173
|
+
config.rescue_standard_error = true
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT License
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Author
|
|
186
|
+
|
|
187
|
+
Manish Prajapati
|
|
@@ -4,12 +4,14 @@ module RailsReactErrors
|
|
|
4
4
|
class Configuration
|
|
5
5
|
attr_accessor :include_full_messages,
|
|
6
6
|
:rescue_standard_error,
|
|
7
|
-
:log_errors
|
|
7
|
+
:log_errors,
|
|
8
|
+
:custom_exceptions
|
|
8
9
|
|
|
9
10
|
def initialize
|
|
10
11
|
@include_full_messages = true
|
|
11
12
|
@rescue_standard_error = false
|
|
12
13
|
@log_errors = true
|
|
14
|
+
@custom_exceptions = {}
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
end
|
|
@@ -9,10 +9,31 @@ module RailsReactErrors
|
|
|
9
9
|
base.rescue_from ActiveRecord::RecordNotFound, with: :handle_record_not_found
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
if defined?(ActiveRecord::RecordInvalid)
|
|
13
|
+
base.rescue_from ActiveRecord::RecordInvalid, with: :handle_record_invalid
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
if defined?(ActionController::ParameterMissing)
|
|
13
17
|
base.rescue_from ActionController::ParameterMissing, with: :handle_parameter_missing
|
|
14
18
|
end
|
|
15
19
|
|
|
20
|
+
base.rescue_from ActiveRecord::RecordNotUnique, with: :handle_conflict if defined?(ActiveRecord::RecordNotUnique)
|
|
21
|
+
|
|
22
|
+
base.rescue_from JSON::ParserError, with: :handle_invalid_json if defined?(JSON::ParserError)
|
|
23
|
+
|
|
24
|
+
RailsReactErrors.configuration.custom_exceptions.each do |klass, config|
|
|
25
|
+
exception_class = Object.const_get(klass)
|
|
26
|
+
base.rescue_from exception_class do |exception|
|
|
27
|
+
render_error(
|
|
28
|
+
message: exception.message,
|
|
29
|
+
code: config[:code],
|
|
30
|
+
status: config[:status]
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
rescue NameError
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
|
|
16
37
|
return unless RailsReactErrors.configuration.rescue_standard_error
|
|
17
38
|
|
|
18
39
|
base.rescue_from StandardError, with: :handle_internal_server_error
|
|
@@ -24,13 +45,20 @@ module RailsReactErrors
|
|
|
24
45
|
render_not_found_error(exception.message)
|
|
25
46
|
end
|
|
26
47
|
|
|
48
|
+
def handle_record_invalid(exception)
|
|
49
|
+
render_validation_error(exception.record)
|
|
50
|
+
end
|
|
51
|
+
|
|
27
52
|
def handle_parameter_missing(exception)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
render_parameter_missing_error(exception.message)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_conflict(exception)
|
|
57
|
+
render_conflict_error(exception.message)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def handle_invalid_json(_exception)
|
|
61
|
+
render_invalid_json_error('Invalid JSON payload')
|
|
34
62
|
end
|
|
35
63
|
|
|
36
64
|
def handle_internal_server_error(exception)
|
|
@@ -22,6 +22,18 @@ module RailsReactErrors
|
|
|
22
22
|
render json: RailsReactErrors::Serializer.server_error(message), status: status
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def render_parameter_missing_error(message, status: :bad_request)
|
|
26
|
+
render json: RailsReactErrors::Serializer.parameter_missing(message), status: status
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render_conflict_error(message, status: :conflict)
|
|
30
|
+
render json: RailsReactErrors::Serializer.conflict(message), status: status
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render_invalid_json_error(message = 'Invalid JSON payload', status: :bad_request)
|
|
34
|
+
render json: RailsReactErrors::Serializer.invalid_json(message), status: status
|
|
35
|
+
end
|
|
36
|
+
|
|
25
37
|
def render_error(message:, code:, status:, errors: {})
|
|
26
38
|
render json: {
|
|
27
39
|
success: false,
|
|
@@ -22,6 +22,18 @@ module RailsReactErrors
|
|
|
22
22
|
new.server_error(message)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def self.parameter_missing(message)
|
|
26
|
+
new.parameter_missing(message)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.conflict(message = 'Conflict')
|
|
30
|
+
new.conflict(message)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.invalid_json(message = 'Invalid JSON payload')
|
|
34
|
+
new.invalid_json(message)
|
|
35
|
+
end
|
|
36
|
+
|
|
25
37
|
def validation(record)
|
|
26
38
|
{
|
|
27
39
|
success: false,
|
|
@@ -67,6 +79,33 @@ module RailsReactErrors
|
|
|
67
79
|
}
|
|
68
80
|
end
|
|
69
81
|
|
|
82
|
+
def parameter_missing(message)
|
|
83
|
+
{
|
|
84
|
+
success: false,
|
|
85
|
+
message: message,
|
|
86
|
+
code: 'PARAMETER_MISSING',
|
|
87
|
+
errors: {}
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def conflict(message)
|
|
92
|
+
{
|
|
93
|
+
success: false,
|
|
94
|
+
message: message,
|
|
95
|
+
code: 'CONFLICT',
|
|
96
|
+
errors: {}
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def invalid_json(message)
|
|
101
|
+
{
|
|
102
|
+
success: false,
|
|
103
|
+
message: message,
|
|
104
|
+
code: 'INVALID_JSON',
|
|
105
|
+
errors: {}
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
|
|
70
109
|
private
|
|
71
110
|
|
|
72
111
|
def serialized_errors(record)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_react_errors
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Manish0407
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activemodel
|