Using External Weather Data In A Custom Panel Extension
Published October 4th, 2023
In this post, you will learn how to fetch data from an external data source and display it in a custom panel extension for Directus Insights.
Panels can only talk to internal Directus services, and can't reliably make external web requests because browser security protections prevent these cross-origin requests from being made. To create a panel that can interact with external APIs, you will create bundle including an endpoint (that can make external requests) and a panel (that uses the endpoint).
Add an Extensions Volume
Follow our Directus Self-Hosted Quickstart, adding a volume for extensions:
volumes:
- ./database:/directus/database
- ./uploads:/directus/uploads
- ./extensions:/directus/extensions
volumes:
- ./database:/directus/database
- ./uploads:/directus/uploads
- ./extensions:/directus/extensions
Create a Bundle
Once you have run docker compose up
for the first time, local directories for the volumes will be created. Navigate to the extensions
directory and use the Directus Extensions CLI to create a bundle:
npx create-directus-extension@latest
├ type: bundle
├ name: directus-extension-bundle-weather
└ language: javascript
npx create-directus-extension@latest
├ type: bundle
├ name: directus-extension-bundle-weather
└ language: javascript
Then, navigate to the newly-created extension directory.
Create an Endpoint
Use npm run add
to add a new extension to the bundle:
npm run add
├ type: endpoint
├ name: weather-endpoint
└ language: javascript
npm run add
├ type: endpoint
├ name: weather-endpoint
└ language: javascript
Navigate to the new weather-endpoint/index.js
file and replace it with the following:
export default {
id: 'weather',
handler: (router) => {
router.get('/', async (req, res) => {
try {
const response = await fetch(`https://api.open-meteo.com/v1/forecast?current_weather=true&${req._parsedUrl.query}`);
if (response.ok) {
res.json(await response.json());
} else {
res.status(response.status).send(response.statusText);
}
} catch(error) {
res.status(500).send(error.message);
}
})
}
}
export default {
id: 'weather',
handler: (router) => {
router.get('/', async (req, res) => {
try {
const response = await fetch(`https://api.open-meteo.com/v1/forecast?current_weather=true&${req._parsedUrl.query}`);
if (response.ok) {
res.json(await response.json());
} else {
res.status(response.status).send(response.statusText);
}
} catch(error) {
res.status(500).send(error.message);
}
})
}
}
From the directus-extension-bundle-weather
directory, run npm run build
. Restart your Directus project, and you should now be able to access http://localhost:8055/weather?longitude=0&latitude=0
.
The Open-Meteo API requires a longitude and latitude, so they must always be provided.
Create a Panel
Use npm run add
to add a new extension to the bundle:
npm run add
├ type: panel
├ name: weather-panel
└ language: javascript
npm run add
├ type: panel
├ name: weather-panel
└ language: javascript
Navigate to the new weather-panel/index.js
file, update the id
to weather-panel
and the name to Weather
.
In the options
array, remove the text
field, and add two new fields for longitude
and latitude
:
options: [
{
field: 'longitude',
name: 'Longitude',
type: 'string',
meta: {
interface: 'input',
width: 'half',
},
},
{
field: 'latitude',
name: 'Latitude',
type: 'string',
meta: {
interface: 'input',
width: 'half',
},
},
],
options: [
{
field: 'longitude',
name: 'Longitude',
type: 'string',
meta: {
interface: 'input',
width: 'half',
},
},
{
field: 'latitude',
name: 'Latitude',
type: 'string',
meta: {
interface: 'input',
width: 'half',
},
},
],
Open panel.vue
, and at the top of the <script>
, import useApi
and Vue's ref
:
import { useApi } from '@directus/extensions-sdk';
import { ref } from 'vue';
export default {
import { useApi } from '@directus/extensions-sdk';
import { ref } from 'vue';
export default {
Then, add longitude
and latitude
props:
props: {
showHeader: {
type: Boolean,
default: false,
},
longitude: {
type: String,
default: '0',
},
latitude: {
type: String,
default: '0',
},
},
props: {
showHeader: {
type: Boolean,
default: false,
},
longitude: {
type: String,
default: '0',
},
latitude: {
type: String,
default: '0',
},
},
Create a setup
method which will run when the panel is loaded:
setup(props) {
const api = useApi();
const weather = ref({});
async function fetchData() {
const response = await api.get(`/weather?longitude=${props.longitude}&latitude=${props.latitude}`);
weather.value = response.data;
}
fetchData();
return { weather };
}
setup(props) {
const api = useApi();
const weather = ref({});
async function fetchData() {
const response = await api.get(`/weather?longitude=${props.longitude}&latitude=${props.latitude}`);
weather.value = response.data;
}
fetchData();
return { weather };
}
This code will immediately fetch weather data using the new internal endpoint, and then make it available as weather
to the template.
Finally, update the template:
<template>
<div class="text" :class="{ 'has-header': showHeader }">
{{ text }}
{{ weather }}
</div>
</template>
<template>
<div class="text" :class="{ 'has-header': showHeader }">
{{ text }}
{{ weather }}
</div>
</template>
Run npm run build
from the bundle directory, and restart Directus.
Use the Panel
Create a new Insights Dashboard and add a Weather panel. Add coordinates, and you should see external data displayed in the panel.
You can now create panels using external data. If you have any questions at all, feel free to join our Discord community.