# External Api call for 3d products

**URL:** <https://forum.8thwall.com/t/external-api-call-for-3d-products/3237>\
**Category:** Technical Support\
**Created:** [October 20, 2024, 3:56pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237 "2024-10-20T15:56:38Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dwayne\_Paisley-Marsh](https://avatars.discourse-cdn.com/v4/letter/d/e19adc/32.png) [@Dwayne\_Paisley-Marsh](https://forum.8thwall.com/u/Dwayne_Paisley-Marsh)\
**Post date:** [October 20, 2024, 3:56pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/1 "2024-10-20T15:56:39Z")

</div>

I’d like to connect to an API and when the AR experience launches I make a call to the API and load it into the scene

import \* as ecs from ‘[@8thwall](https://forum.8thwall.com/groups/8thwall)/ecs’

const {XR8} = window as any

ecs.registerComponent({  
name: ‘random-model-api’,  
schema: {},  
schemaDefaults: {},  
data: {  
modelUrl: ecs.string,  
name: ecs.string,  
},  
add: (world, component) =\> {  
fetch(‘[https://immersive-commerce.vercel.app/api/models](https://immersive-commerce.vercel.app/api/models)’)  
.then(res =\> res.json())  
.then((data) =\> {  
// Select a random model from the fetched data  
const randomIndex = Math.floor(Math.random() \* data.length)  
const selectedModel = data[randomIndex]

```
    // Set model data
    component.data.modelUrl = selectedModel.glbUrl
    component.data.name = selectedModel.name

    // Create an entity for the selected model
    const entity = world.createEntity()

    // Add components to the entity
    entity.addComponent(ecs.GltfModel, {
      url: component.data.modelUrl,
      scale: {x: 0.5, y: 0.5, z: 0.5},
    })
    entity.addComponent(ecs.Position, {
      x: 0,
      y: 0,
      z: -3,
    })
    entity.addComponent(ecs.Collider, {
      type: 'box',
    })

    // Check if the GLTF model was loaded
    world.on(ecs.events.GLTF_MODEL_LOADED, (event) => {
      if (event.model === entity.getComponent(ecs.GltfModel).model) {
        console.log(`Model loaded: ${component.data.name}`)
      }
    })

    // Update UI with the model name
    ecs.Ui.mutate(world, component.eid, (cursor) => {
      const text = `Loaded Model: ${component.data.name}`
      cursor.text = text
      cursor.width = text.length * cursor.fontSize * 0.2
      return false
    })
  })
  .catch((e) => {
    console.error('Error fetching 3D models:', e)
  })

// Center the view on click or touch
window.addEventListener('click', XR8.XrController.recenter)
window.addEventListener('touchstart', XR8.XrController.recenter)

```

},  
tick: (world, component) =\> {  
// Logic to be executed on each tick if necessary  
},  
remove: (world, component) =\> {  
// Cleanup logic here if necessary  
},  
})

This is my code, but it’s not working I get the error entity.addComponent is not a function

---

<div class="post-metadata">

**Author:** ![Dwayne\_Paisley-Mars1](https://avatars.discourse-cdn.com/v4/letter/d/ba9def/32.png) [@Dwayne\_Paisley-Mars1](https://forum.8thwall.com/u/Dwayne_Paisley-Mars1)\
**Post date:** [October 20, 2024, 8:46pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/2 "2024-10-20T20:46:19Z")

</div>

Okay so I’ve got my API call working but the 3D model part isnt’ working

```ts
import * as ecs from '@8thwall/ecs'

const {XR8} = window as any

ecs.registerComponent({
  name: 'random-model-api',
  schema: {},
  schemaDefaults: {},
  data: {
    glbUrl: ecs.string,
    name: ecs.string,
  },
  add: (world, component) => {
    // Fetch the 3D models from the API
    fetch('https://immersive-commerce.vercel.app/api/models')
      .then(res => res.json())
      .then((data) => {
        // console.log('API Response:', JSON.stringify(data, null, 2))

        // Select a random model from the fetched data
        const randomIndex = Math.floor(Math.random() * data.length)
        const selectedModel = data[randomIndex]
        console.log('Selected:', JSON.stringify(selectedModel, null, 2))

        // Set the model URL and name in the component's data
        component.data.glbUrl = selectedModel.glbUrl
        component.data.name = selectedModel.name

        console.log('Selected:', JSON.stringify(selectedModel.glbUrl, null, 2))

        // Create a new entity in ECS
        const entity = world.createEntity()
        console.log('Created entity:', entity) // Debug entity creation

        if (entity && typeof entity.addComponent === 'function') {
          // Add a GLTF model component to the entity
          entity.addComponent(ecs.GltfModel, {
            url: component.data.glbUrl,
            scale: {x: 0.5, y: 0.5, z: 0.5},
          })
          console.log('GLTF Model Component added with URL:', component.data.glbUrl)

          // Add a position component to the entity
          entity.addComponent(ecs.Position, {
            x: 0,
            y: 0,
            z: -3,
          })

          // Add a collider component for interaction
          entity.addComponent(ecs.Collider, {
            type: 'box',
          })

          // Listen for the model load event
          world.on(ecs.events.GLTF_MODEL_LOADED, (event) => {
            console.log('GLTF_MODEL_LOADED event:', event)
            if (event.model === entity.getComponent(ecs.GltfModel).model) {
              console.log(`Model loaded: ${component.data.name}`)
            }
          })

          // Update the ECS UI to show the model's name
          ecs.Ui.mutate(world, component.eid, (cursor) => {
            const text = `Loaded Model: ${component.data.name}`
            cursor.text = text
            cursor.width = text.length * cursor.fontSize * 0.2

            return false
          })
        } else {
          console.error('Entity creation failed or addComponent is not a function')
        }
      })
      .catch((e) => {
        console.error('Error fetching 3D models:', e)
      })

    // Center the view on click or touch
    window.addEventListener('click', XR8.XrController.recenter)
    window.addEventListener('touchstart', XR8.XrController.recenter)
  },
  tick: (world, component) => {
    // Logic to be executed on each tick if necessary
  },
  remove: (world, component) => {
    // Cleanup: Remove event listeners
    window.removeEventListener('click', XR8.XrController.recenter)
    window.removeEventListener('touchstart', XR8.XrController.recenter)

    // Optionally remove the entity from the ECS world
    const entity = world.getEntity(component.eid)
    if (entity) {
      world.deleteEntity(entity)
    }
  },
})

```

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [October 21, 2024, 4:07pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/3 "2024-10-21T16:07:20Z")

</div>

Hi! I’ll take a look and help you get this figured out. In the meantime I’ve edited your post to add code formatting to make it easier to read.

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [October 21, 2024, 4:22pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/4 "2024-10-21T16:22:41Z")

</div>

@Dwayne_Paisley-Marsh

There were a couple issues with your component so I went ahead and mostly re-did it. The edits should provide more context to how ECS works in Niantic Studio.

```ts
import * as ecs from '@8thwall/ecs'

const {XR8} = window as any

ecs.registerComponent({
  name: 'random-model-api',
  schema: {},
  schemaDefaults: {},
  data: {
    glbUrl: ecs.string,
    name: ecs.string,
  },
  add: (world, component) => {
    const {eid} = component

    fetch('https://immersive-commerce.vercel.app/api/models')
      .then(res => res.json())
      .then((data) => {
        const randomIndex = Math.floor(Math.random() * data.length)
        const selectedModel = data[randomIndex]

        component.data.glbUrl = selectedModel.glbUrl
        component.data.name = selectedModel.name

        const entity = world.createEntity()

        ecs.GltfModel.set(world, entity, {
          url: component.data.glbUrl,
        })

        ecs.Scale.set(world, entity, {
          x: 0.5,
          y: 0.5,
          z: 0.5,
        })

        world.setPosition(entity, 0, 0, -3)

        ecs.Collider.set(world, entity, {
          shape: ecs.ColliderShape.Box,
        })

        world.events.addListener(entity, ecs.events.GLTF_MODEL_LOADED, () => {
          console.log(component.data.name)

          ecs.Ui.set(world, eid, {
            text: component.data.name,
            width: component.data.name.length * 16 * 0.2,
          })
        })
      })
      .catch((e) => {
        console.error('Error fetching 3D models:', e)
      })

    // Center the view on click or touch
    window.addEventListener('click', XR8.XrController.recenter)
    window.addEventListener('touchstart', XR8.XrController.recenter)
  },
  tick: (world, component) => {
    // Logic to be executed on each tick if necessary
  },
  remove: (world, component) => {
    // Cleanup: Remove event listeners
    window.removeEventListener('click', XR8.XrController.recenter)
    window.removeEventListener('touchstart', XR8.XrController.recenter)
  },
})

```

---

<div class="post-metadata">

**Author:** ![Dwayne\_Paisley-Marsh](https://avatars.discourse-cdn.com/v4/letter/d/e19adc/32.png) [@Dwayne\_Paisley-Marsh](https://forum.8thwall.com/u/Dwayne_Paisley-Marsh)\
**Post date:** [October 22, 2024, 6:51am UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/5 "2024-10-22T06:51:14Z")

</div>

OMG thank you so so much.

This was the cherry on top that I needed.

However, I’d like to load all the models not just a random one 🙂

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [October 22, 2024, 1:01pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/6 "2024-10-22T13:01:39Z")

</div>

Would you load all the models into one position?

---

<div class="post-metadata">

**Author:** ![Dwayne\_Paisley-Marsh](https://avatars.discourse-cdn.com/v4/letter/d/e19adc/32.png) [@Dwayne\_Paisley-Marsh](https://forum.8thwall.com/u/Dwayne_Paisley-Marsh)\
**Post date:** [October 22, 2024, 1:14pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/7 "2024-10-22T13:14:38Z")

</div>

Hmm no in a circle on the z axis

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [October 22, 2024, 4:09pm UTC](https://forum.8thwall.com/t/external-api-call-for-3d-products/3237/8 "2024-10-22T16:09:47Z")

</div>

Try this

```ts
import * as ecs from '@8thwall/ecs'

const {XR8} = window as any

ecs.registerComponent({
  name: 'random-model-api',
  schema: {},
  schemaDefaults: {},
  data: {},
  add: (world, component) => {
    fetch('https://immersive-commerce.vercel.app/api/models')
      .then(res => res.json())
      .then((data) => {
        const numModels = data.length
        const radius = 3 // Radius
        const angleIncrement = (2 * Math.PI) / numModels

        data.forEach((modelData, index) => {
          const angle = angleIncrement * index
          const x = radius * Math.cos(angle)
          const z = radius * Math.sin(angle)
          const y = 0 // Height

          const entity = world.createEntity()

          ecs.GltfModel.set(world, entity, {
            url: modelData.glbUrl,
          })

          ecs.Scale.set(world, entity, {
            x: 0.5,
            y: 0.5,
            z: 0.5,
          })

          world.setPosition(entity, x, y, z)

          ecs.Collider.set(world, entity, {
            shape: ecs.ColliderShape.Box,
          })

          world.events.addListener(entity, ecs.events.GLTF_MODEL_LOADED, () => {
            console.log(modelData.name)

            ecs.Ui.set(world, entity, {
              type: '3d',
              text: modelData.name,
              width: modelData.name.length * 12,
            })
          })
        })
      })
      .catch((e) => {
        console.error('Error fetching 3D models:', e)
      })

    // Center the view on click or touch
    window.addEventListener('click', XR8.XrController.recenter)
    window.addEventListener('touchstart', XR8.XrController.recenter)
  },
  tick: (world, component) => {
    // Logic to be executed on each tick if necessary
  },
  remove: (world, component) => {
    // Cleanup: Remove event listeners
    window.removeEventListener('click', XR8.XrController.recenter)
    window.removeEventListener('touchstart', XR8.XrController.recenter)
  },
})

```
