Search code examples
reactjsmongodbaxiosgraphqlcloudinary

REACT/Mongo/GraphQL: How do I capture the URL from a Cloudinary response, before saving the document to MongoDB


I can successfully create a new document to save to my MongoDB, while at the same time sending a picture to Cloudinary.

However, I would like to save the URL sent in the response from Cloudinary into my document, but the document is saved before the url is set.

Any ideas on how I can successfully capture the url before the document is created?

This is the form I use to make a mutation and upload the photo to cloudinary (slightly edited for brevity):

export default function AddPlantModal() {
  const [name, setName] = useState("");
  const [description, setDescription] = useState("");

// // // // here is the state for the URL // // // //
  const [publicURL, setPublicURL] = useState("");
// ********************************************** //

  const [status, setStatus] = useState("healthy");
  const [clientID, setClientID] = useState("");
  const [photo, setPhoto] = useState("");

  const [addPlant] = useMutation(ADD_PLANT, {
    variables: {
      name,
      description,
      publicURL,
      status,
      clientID,
    },
    update(cache, { data: { addPlant } }) {
      const { plants } = cache.readQuery({ query: GET_PLANTS });
      cache.writeQuery({
        query: GET_PLANTS,
        data: { plants: [...plants, addPlant] },
      });
    },
  });

  const { loading, error, data } = useQuery(GET_CLIENTS);

/* here is where I try to set the state of publicURL with the response from Cloudinary */

  const UploadImage = async () => {
    const formData = new FormData();
    formData.append("file", photo);
    formData.append("upload_preset", UPLOAD_PRESET);

    await axios
      .post(
        `https://api.cloudinary.com/v1_1/${CLOUD_NAME}/image/upload`,
        formData
      )
      .then((response) => {
        setPublicURL(response.data.url);
      });
  };
// ********************************************** //

  const HandleSubmit = (e) => {
    e.preventDefault();

    UploadImage();

    if (name === "" || clientID === "") {
      return alert("please provide name and client");
    }

    addPlant(name, description, clientID, publicURL, status);
    setName("");
    setDescription("");
    setStatus("healthy");
    setClientID("");
    setPhoto("");
  };

  if (loading) return null;
  if (error) return error.message;

  return (
    <>
      {!loading && !error && (
        <>
          <button>
            <...> //button that launches modal
          </button>

          <div
         
                  <h1 className="modal-title fs-5" id="addProjectModalLabel">
                    Add a Plant
                  </h1>
                  <button> Close </button>
                </div>
                <div className="modal-body">
                  <form onSubmit={HandleSubmit}>
                    <div className="mb-3">
                      
                      <input
                        type="text"
                        className="form-control"
                        id="name"
                        value={name}
                        onChange={(e) => setName(e.target.value)}
                      />
                      
                      <input
                        type="file"
                        className="form-control"
                        id="photo"
                        onChange={(e) => {
                          setPhoto(e.target.files[0]);
                        }}
                      />
                    </div>
                    <div className="mb-3">
                      
                      <textarea
                        className="form-control"
                        id="description"
                        value={description}
                        onChange={(e) => setDescription(e.target.value)}
                      ></textarea>
                    </div>
                    <div className="mb-3">
                     
                      <select
                        id="status"
                        className="form-select"
                        value={status}
                        onChange={(e) => setStatus(e.target.value)}
                      >
                        <option value="suffering">Suffering</option>
                        <option value="healthy">Healthy</option>
                        <option value="thriving">Thriving</option>
                        <option value="nursing">Nursing</option>
                      </select>

                      <div className="mb-3">
                        
                        <select
                          id="clientId"
                          className="form-select"
                          value={clientID}
                          onChange={(e) => setClientID(e.target.value)}
                        >
                          <option value="">Select Client</option>
                          {data.clients.map((client) => (
                            <option key={client.id} value={client.id}>
                              {client.name}
                            </option>
                          ))}
                        </select>
                      </div>

                      <button
                        className="btn btn-primary"
                        type="submit"
                        data-bs-dismiss="modal"
                        // onClick={UploadImage}
                      >
                        Submit
                      </button>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </>
      )}
    </>
  );
}

here you can see in the resulting page that the document was created without the url:


plant: client: {__typename: 'Client', id: '6347df99fd13d79fbbc900ee', name: 'Owen', email:'[email protected]', phone: '+55 5 55555555'}
description: "7"
id: "63482c133b5c3c96c1789xbb"
name: "7"
publicURL: ""
status: "Healthy"

Solution

  • I'm also using Cloudinary with React.

    1. Don't bother with the publicUrl - just use the publicId and then build the image URL dynamically using the @cloudinary/base package. i.e. don't store image URLs in your db, store cloudinary IDs.
    2. Perform the mutation at the end of the upload (ex: in the .then())so that you already have the publicId by the time you need it.