Why isn't my Unity Web Request running in WebGL build?
Tags: webgl, unity3d, api, firebase
Published: 2021-12-16 11:40:01

I decided to pick up Unity3D again to get back into the Unity dev scene and to put my (chaotic) creativity to good use. In doing so, I made this:

![what am I doing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfu4cyctfvqx94xi1gtb.png)

This was something so trivial ... so minimal in effort ... so not-complicated that I thought I could pull it off in 1 night after a long day of work. Ho boy was I wrong. 

My project was simple. Use a free, hosted API to send requests and return the response to a 2d UI text object. Add some 3d scenery and boom, that should be it. After running this locally in the editor and Mac standalone builds, I was rolling with success. 

What I did not expect was for my WebGL build to behave differently to the standalone/editor build. When running the app in WebGL, I get these error logs when running my web request:

![Of CORS there's something wrong](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhtzo8loxzabth0g2nar.png)

Now I hadn't used Unity in a while, let alone coding web requests. To make my life easier, I summarized my high priority items: 

1. Find a cleaner method to send unity web requests.
2. How on earth do I fix `Access-Control-Allow-Origin`?!

I do remember hearing about `CORS` headers back round 2016 when I was last using Unity3D + WebGL on the job ... maybe it's the same thing? 😅

Reading through forums, blog posts and Unity's own scripting manual was mind blowing and mind expanding. I eventually encountered better (and cleaner) methods to call APIs in Unity. Below is a before and after of my request code:

## Before 
```
public class ApiController : MonoBehaviour {

    string url = "https://dog-api.kinduff.com/api/facts";

    [SerializeField]
    Text textObject = null;

    [Serializable]
    public class JsonResponse {
        public List<string> facts;
    }

    [SerializeField]
    String fact = "";

    void Start () {
        StartCoroutine (callApi ());
    }

    IEnumerator callApi () {
        UnityWebRequest uwr = UnityWebRequest.Get (url);
        yield return uwr.SendWebRequest ();
        if (uwr.isHttpError) {
            Debug.Log ("Error While Sending: " + uwr.error);
        } else {
            Debug.Log ("Received: " + uwr.downloadHandler.text);
        }
        JsonResponse info = JsonUtility.FromJson<JsonResponse> (uwr.downloadHandler.text);
        Debug.Log (info.facts[0]);
        textObject.text = info.facts[0];
        fact = info.facts[0];
    }
}
```


## After

```
public class DogApi : MonoBehaviour {
    private const string URL = "https://dog-api.kinduff.com/api/facts";

    [SerializeField]
    private Text _textObject;

    public void GenerateRequest () {
        StartCoroutine (ProcessRequest (URL));
    }

    private IEnumerator ProcessRequest (string uri) {
        using (UnityWebRequest request = UnityWebRequest.Get (uri)) {
            request.SetRequestHeader("Access-Control-Allow-Origin", "*");
            yield return request.SendWebRequest ();

            if (request.isNetworkError) {
                Debug.Log (request.error);
            } else {
                JSONNode factsResponse = JSON.Parse(request.downloadHandler.text);
                Debug.Log(factsResponse["facts"][0]);
                _textObject.text = factsResponse["facts"][0];
            }
        }
    }
}
```

After tidying up the code it was time to try fix the CORS issue. Now I did a ton of reading. Many tabs were open. Many lines were read. I had a few nights reading topics such as:

* CORS
* server response headers
* firing api requests from a browser instead of a server

Finally it was clicking to me. The server hosting the API `https://dog-api.kinduff.com/api/facts` was fine. The web browser requesting the API endpoint was fine. However, for security purposes, the hosting server was not configured in such a way to allow any ol' random domain to request information from it. i.e the API is public, and is accessible from a browser however my WebGL build (hosted now on firebase) is not permitted by the hosting API server to access to the API. 

For the server to accept my request, I would need to add a header to the API response itself, with the header and its value being `Access-Control-Allow-Origin: myFirebaseDomain`.
 
Now I could go about this and host the API myself on my own infra however ... that was not the intention of this project and I get what I need (well in editor/standalone at least) from the live API. If I decide to add more time and effort to this project, for sure I'd love to take that one step further and host the API myself. Now is not the time though.

![phew](https://i.gifer.com/2bu.gif)

Now I hope someone finds this useful. I wanted to cover this journey of mine as it started really small and expanded into this midnight troubleshooting rhythm. Capturing these investigations helps me solidy what I have learned. If my terminology or explanation of CORS headers is incorrect, I do apologise. I am really tired and am a simple man.