How do I format plain query to hierarchy json object?

I want to format plain query to hierarchy json to display on front-end but I don’t know how

I may have relational database that has Table called “Todo” which has following column

  1. ID
  2. Name
  3. ParentID

for example

[
    {id: 2, Name:"Task 1.1", ParentID: 1},
    {id: 5, Name:"Task 2", ParentID: null},
    {id: 6, Name:"Task 2.1", ParentID: 5},
    {id: 1, Name:"Task 1", ParentID: null},
    {id: 3, Name:"Task 1.2", ParentID: 1},
    {id: 4, Name:"Task 1.3", ParentID: 1},
    {id: 7, Name:"Task 2.2", ParentID: 5},
    {id: 8, Name:"Task 2.3", ParentID: 5},
    {id: 9, Name:"Task 3", ParentID: null}
]

to

[
  {
    "id": 5,
    "Name": "Task 2",
    "ParentID": null,
    "subtasks": [
      {
        "id": 6,
        "Name": "Task 2.1",
        "ParentID": 5,
        "subtasks": []
      },
      {
        "id": 7,
        "Name": "Task 2.2",
        "ParentID": 5,
        "subtasks": []
      },
      {
        "id": 8,
        "Name": "Task 2.3",
        "ParentID": 5,
        "subtasks": []
      }
    ]
  },
  {
    "id": 1,
    "Name": "Task 1",
    "ParentID": null,
    "subtasks": [
      {
        "id": 2,
        "Name": "Task 1.1",
        "ParentID": 1,
        "subtasks": []
      },
      {
        "id": 3,
        "Name": "Task 1.2",
        "ParentID": 1,
        "subtasks": []
      },
      {
        "id": 4,
        "Name": "Task 1.3",
        "ParentID": 1,
        "subtasks": []
      }
    ]
  },
  {
    "id": 9,
    "Name": "Task 3",
    "ParentID": null,
    "subtasks": []
  }
]

For two level (root and children) my attempt is to push Todo that has no parent ID (root) into an array and then push Todos that have parent into the parent todo

I have tried 2 levels hierarchy and I think it works fine but don’t know how to implement more level in my next.js project and I’m not quite sure this is good practice or not.

here the code I have tried.

const data = [
    {id: 2, Name:"Task 1.1", ParentID: 1},
    {id: 5, Name:"Task 2", ParentID: null},
    {id: 6, Name:"Task 2.1", ParentID: 5},
    {id: 1, Name:"Task 1", ParentID: null},
    {id: 3, Name:"Task 1.2", ParentID: 1},
    {id: 4, Name:"Task 1.3", ParentID: 1},
    {id: 7, Name:"Task 2.2", ParentID: 5},
    {id: 8, Name:"Task 2.3", ParentID: 5},
    {id: 9, Name:"Task 3", ParentID: null},
];

function buildHierarchy(data) {
    let result = [];
    let todos = [...data];

    // construct root level
    todos.forEach((task)=>{
        if(task.ParentID === null){
            const new_task = {...task, subtasks:[]}

            result.push(new_task)
        }
    })

    // constrct child level
    result.forEach((parent, index)=>{
        todos.forEach((task)=>{
            if(task.ParentID === parent.id){
                const new_task = {...task, subtasks:[]}
    
                parent.subtasks.push(new_task)
            }
        })
    })
    
    return result
}

const result = buildHierarchy(data);
console.log(JSON.stringify(result,null,2));