Сначала надо превратить плоский массив во вложенный:
function createTreeData(data, idProp, parentProp) {
const tree = Object.fromEntries(data.map(n => [ n[idProp], { ...n, children: [] } ]));
return Object
.values(tree)
.filter(n => !(tree[n[parentProp]] && tree[n[parentProp]].children.push(n)));
}
Затем из вложенного массива собрать разметку:
const createTreeHTML = data =>
Array.isArray(data) && data.length
? `<ul>${data.map(n => `
<li>
${n.name}
${createTreeHTML(n.children)}
</li>`).join('')}
</ul>`
: '';
Вот так всё просто получается:
const treeData = createTreeData(obj, 'id', 'parent_id');
const treeHTML = createTreeHTML(treeData);
document.body.insertAdjacentHTML('beforeend', treeHTML);