For my project, I'm trying to add a profile picture, sent from a React frontend to a Rails API backend by leveraging ActiveStorage. I'm already using the devise and devise-jwt gems for authentication.
I'm having trouble understanding how to customize the devise user create action, user Model, and user_controller to accept a profile picture. I can set up the ActiveStorage migrations and run them fine.
The other main issue is how to customize my POST request from my React frontend. The guides linked above (and from my other searches) indicate that I need to create a new FormData object and should not specify a 'Content-Type' header since the FormDataAPI will handle everything so we post this in the body of the request like this:
`
fetch('http://localhost:3000/posts', {
method: 'POST',
body: formData
})
`
However, the way I have it set up in my frontend React code is formatted like this so that the devise gem on my Rails API backend will understand where to get what data to create a user: `
fetch("http://localhost:3000/signup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
user: {
username: username,
email: email,
password: password,
}
})
`
Where the body is stringified with the JSON Object: `
{
user: {
username: username,
email: email,
password: password,
}
}
`
I'm having trouble reconciling this with the devise gem in my Rails API backend and haven't found a way to integrate this so I need a way to customize the create action in the devise registration controller to include a profile picture association in ActiveStorage.
So far my Rails controllers looks like this:
`
class Users::RegistrationsController < Devise::RegistrationsController
respond_to :json
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response
rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response
rescue_from JWT::ExpiredSignature, with: :expired_token_response
private
def respond_with(resource, _opts = {})
if resource.persisted?
render json: {
status: {code: 200, message: 'Signed up sucessfully.'},
# data: UserSerializer.new(resource).serializable_hash[:data][:attributes]
data: UserSerializer.new(resource)
}
else
render json: {
status: {message: "User couldn't be created successfully. #{resource.errors.full_messages.to_sentence}"}
}, status: :unprocessable_entity
end
end
end
`
And my routes.rb for users looks like this:
`
devise_for :users, path: '', path_names: {
sign_in: 'login',
sign_out: 'logout',
registration: 'signup'
},
controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
`
I've tried reformatting my frontend fetch request like this:
`
async function exampleSubmit(e) {
e.preventDefault();
setLoading(true);
const form = document.getElementById('sign-up-form');
const formData = new FormData(form);
const userObj = {
username: formData.get('username'),
email: formData.get('email'),
password: formData.get('password'),
};
let photoInput = document.getElementById('profile-photo-input');
if (photoInput.files[0]) {
const uploadedPicture = photoInput.files[0];
userObj['profilePicture'] = uploadedPicture;
}
await fetch("http://localhost:3000/signup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
user: userObj
}),
})
.then(res => res.json())
.then((data) => {
console.log(data);
});
}
`
I'm wondering if this will work as well: `
async function exampleSubmit(e) {
e.preventDefault();
setLoading(true);
const form = document.getElementById('sign-up-form');
const formData = new FormData(form);
let photoInput = document.getElementById('profile-photo-input');
if (photoInput.files[0]) {
const uploadedPicture = photoInput.files[0];
formData.append('profilePicture', uploadedPicture);
}
await fetch("http://localhost:3000/signup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
user: formData
}),
})
.then(res => res.json())
.then((data) => {
console.log(data);
});
}
`
Or if this will throw an error in the backend if Rails cannot recognize the formData in their params when receiving the POST request.
As for the Rails API Backend, I need some help in understanding how to customize the user with strong params to incl an ActiveStorage association + a way to give someone a default profile picture.
Thanks so much!