DEV Community

Cover image for Making a REST service using Node and Express to use with Unity - Part 3
Cem Ugur Karacam
Cem Ugur Karacam

Posted on

Making a REST service using Node and Express to use with Unity - Part 3

Greetings to all unity ninjas!

This part we'll make a POST request from a unity client to the node server. We haven't made a database, yet, so I'll make an array for now.

Start with server-side. In app.js, remember that we had a single enemy object, this part, I'll make an array and populate with some enemy.

let enemies = [
    {
        "id": 0,
        "name": "orc",
        "health": 100,
        "attack": 25
    },
    {
        "id": 1,
        "name": "wolf",
        "health": 110,
        "attack": 25
    }
];
Enter fullscreen mode Exit fullscreen mode

Next, tell express to support JSON-encoded bodies.

app.use(express.json());
Enter fullscreen mode Exit fullscreen mode

Next, let's make a post method to receive a request from unity client.

app.post('/enemy/create', (req, res) => {
let newEnemy = {
    "id": req.body.id,
    "name": req.body.name,
    "health": req.body.health,
    "attack": req.body.attack
};

enemies.push(newEnemy);
console.log(enemies);
res.send(enemies);
});
Enter fullscreen mode Exit fullscreen mode

And I'll modify my get method to fetch my enemies array. Now my app.js file looks like this:

const express = require('express');
const app = express();
app.use(express.json());

app.get('/', (req, res) => {
    res.send('Hello Unity Developers!');
});

let enemies = [
    {
        "id": 0,
        "name": "orc",
        "health": 100,
        "attack": 25
    },
    {
        "id": 1,
        "name": "wolf",
        "health": 110,
        "attack": 25
    }
];

app.get('/enemy', (req, res) => {
    res.send(enemies);
});

app.post('/enemy/create', (req, res) => {
let newEnemy = {
    "id": req.body.id,
    "name": req.body.name,
    "health": req.body.health,
    "attack": req.body.attack
};

enemies.push(newEnemy);
console.log(enemies);
res.send(enemies);
});

app.listen(3000, () => console.log('started and listening.'));
Enter fullscreen mode Exit fullscreen mode

Open a terminal, start node and it's time to visit unity client.

node app.js
Enter fullscreen mode Exit fullscreen mode

Let's make an api call, I'll use postman.

Alt Text

Don't forget to set header and body to application/json.

Now make an enemy json string based on the enemy schema and hit to send.

Alt Text

Alt Text

Seems our enemies growing :)

Last part, we have successfully received a json object and displayed it to screen with unity ui elements. But this part our structure has changed a bit. I've added id property to the enemy and my response now not an object but an array.

First, add id property to Enemy class in unity.

public class Enemy
{
    public int id;
    public string name;
    public int health;
    public int attack;
}
Enter fullscreen mode Exit fullscreen mode

Yeah, that was easy! But keep in mind, JsonUtility does not work with {get; set;}.

Now the important part! If your task is to send a json array and parse in unity, there is no out of the box solution with JsonUtility, unfortunately. Don't worry, after a couple googling and experimenting, I found a workaround, for a detailed explanation, take a look here and here.

For example, If your response is like this, then JsonUtility won't work:

[
  {
    //item
  },
  {
    //item
  },
  {
    //item
  }
]
Enter fullscreen mode Exit fullscreen mode

Instead, we'll make it a single key and an array object as a value:

{
    "result": [{//item},{//item},{//item}]
}
Enter fullscreen mode Exit fullscreen mode

This way, we have a one list of items and this structure works with JsonUtility.

To make this trick possible we'll make a static class named JsonHelper. Let's create it in the project folder.

using UnityEngine;
using System.Collections.Generic;

public static class JsonHelper
{
    public static List<T> FromJson<T>(string json)
    {
        Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(json);
        return wrapper.result;
    }

    [System.Serializable]
    private class Wrapper<T>
    {
        public List<T> result;
    }
}
Enter fullscreen mode Exit fullscreen mode

JsonHelper class has a generic FromJson method that returns a list of any class we're going to make. Here key part is the Wrapper class that has a result list that stores the values from. In our case, when we get an array of json objects from server, we have to modify our response string like this:

{
    "result": [
        //enemy,
        //enemy
    ]
}
Enter fullscreen mode Exit fullscreen mode

We have discussed our schema and made a helper class to make it work, let's write some code!

First, mark the Enemy class as Serializable, so unity will be able to convert to json. More explanation here.

[System.Serializable]
public class Enemy
{
    public int id;
    public string name;
    public int health;
    public int attack;
}
Enter fullscreen mode Exit fullscreen mode

Second, open ClientApi script and make a post method. It will take two parameters, post url and an enemy object to feed in unity's post method.

public IEnumerator Post(string url, Enemy enemy)
{
  var jsonData = JsonUtility.ToJson(enemy);
  Debug.Log(jsonData);

  using(UnityWebRequest www = UnityWebRequest.Post(url, jsonData))
  {

  }
}
Enter fullscreen mode Exit fullscreen mode

We are converting an Enemy object to a json string with JsonUtility.

Next, configure our request's content-type.

public IEnumerator Post(string url, Enemy enemy)
{
  var jsonData = JsonUtility.ToJson(enemy);
  Debug.Log(jsonData);

  using(UnityWebRequest www = UnityWebRequest.Post(url, jsonData))
  {
        www.SetRequestHeader("content-type", "application/json");
    www.uploadHandler.contentType = "application/json";
    www.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));

    yield return www.SendWebRequest();
  }
}
Enter fullscreen mode Exit fullscreen mode

We have set our method to send a json object and to a url endpoint.

Remember that, we send back our enemies as a response after request. So, let's make a list from the response, then display to console. Now, time to handle response end error.

// handle the result
var result = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);  
result = "{\"result\":" + result + "}"; 
var resultEnemyList = JsonHelper.FromJson<Enemy>(result);

foreach (var item in resultEnemyList)
{
  Debug.Log(item.name);
}
Enter fullscreen mode Exit fullscreen mode

This is the part that we have discussed above. As soon as I get data, I modified it to be able to work with JsonHelper.

Then just display to console each element's name to test.

public IEnumerator Post(string url, Enemy enemy)
{
  var jsonData = JsonUtility.ToJson(enemy);
  Debug.Log(jsonData);

  using(UnityWebRequest www = UnityWebRequest.Post(url, jsonData))
  {
        www.SetRequestHeader("content-type", "application/json");
    www.uploadHandler.contentType = "application/json";
    www.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));
    yield return www.SendWebRequest();

    if (www.isNetworkError)
    {
      Debug.Log(www.error);
    }
    else
    {
      if (www.isDone)
      {
        // handle the result
        var result = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);  
        result = "{\"result\":" + result + "}"; 
        var resultEnemyList = JsonHelper.FromJson<Enemy>(result);

        foreach (var item in resultEnemyList)
        {
          Debug.Log(item.name);
        }
      }
      else
      {
        //handle the problem
        Debug.Log("Error! data couldn't get.");
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

To test, define a string variable for post url, make an Enemy object in Start and switch back to unity.

public string getUrl  = "localhost:3000/enemy";
public string postUrl = "localhost:3000/enemy/create";

void Start()
{
  var enemy = new Enemy(){
    id = 100,
    name = "Balrog",
    health = 1000,
    attack = 2500
  };

  StartCoroutine(Post(postUrl, enemy));
}
Enter fullscreen mode Exit fullscreen mode

Time to hit Play!

Alt Text

Check console.log on terminal too.

Alt Text

Good job, our unity ninja skills improved a bit more!!πŸŽ‰πŸŽ‰πŸŽ‰πŸŒŸπŸŒŸπŸŒŸπŸ‘πŸ‘πŸ‘πŸ˜ŽπŸ˜Ž

Looks like we have successfully sent data from unity client, received from the server and responded with updated data πŸ˜ŽπŸ‘Ύ

Grab the code here

Next part we'll post with the ui element's help and to handle data in unity, we'll use Scriptable Objects.

Until the next part, cheers!

Oldest comments (2)

Collapse
 
jdnichollsc profile image
J.D Nicholls

Hey mate, excellent post! About Unity, check this wrapper for the UnityWebRequest to use Promises instead of Coroutines github.com/proyecto26/RestClient
Best!

Collapse
 
cemuka profile image
Cem Ugur Karacam

Thanks!
That's a pretty good wrapper!