Skip to content
On this page

Developer Blog

Using External Weather Data In A Custom Panel Extension

Published October 4th, 2023

Written By
Kevin Lewis
Kevin Lewis
Director, Developer Relations

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:

js
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:

js
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:

js
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:

js
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:

js
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:

vue
<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.

Panel configutation showing a longitude and latitude input field

A panel showing a JSON payload of weather data

You can now create panels using external data. If you have any questions at all, feel free to join our Discord community.

Make it count

How helpful was this article?