The missing WeatherKit REST API Docs
Tags: javascript, api, webdev, tutorial
Published: 2022-06-10 09:23:55

{% embed https://www.youtube.com/watch?v=7mg42_Fix9k %}

[WeatherKit](https://developer.apple.com/weatherkit/get-started/) is Apple's new weather data service, announced at WWDC 2022 it is the their answer to privacy focussed weather data. Not only have they created this for use in native iOS Swift apps but they have also created a REST API so that web developers are able to access it as well.

The [WeatherKit REST API Documentation](https://developer.apple.com/documentation/weatherkitrestapi) is fairly limited on how to get this set up so here is a guide to get up you up and running with Apple's WeatherKit. In fact, I had a labs call with two Apple engineers to get this working which was pretty cool. We will build a simple ExpressJS server and a index.html page to display weather for a set location.

## WeatherKit REST API setup

The REST API requires a signed JWT to be attached to each request. To set this up you need:

- A paid developer account
- An App Identifier
- A key

You can set up an App Identifier on the [Identifiers](https://developer.apple.com/account/resources/identifiers/list) page of your developer account. You need to create a new `App ID` of type App, give it a `Bundle ID` in reverse-domain name style, so `com.myapp.weather` or similar, and then make sure you select WeatherKit from the App Services tab. This App Identifier can take about 30 minutes to propagate through Apple's systems.

For the key you go to the [Keys](https://developer.apple.com/account/resources/authkeys/list) page in your developer account and add a new key with WeatherKit selected. Remember to download the key file you get at the end!

## WeatherKit REST API JWT

Now you have your key and App Identifier we can create a server to sign that key. You will be signing the JWT yourself so you will need a backend/server where you can execute your code, as always **do not expose your key to the browser or client**

So, let's create a little ExpressJS server. Comments in the code explain what is going on. I'm assuming you know how to use [ExpressJS](https://expressjs.com/) already.

```javascript
const express = require("express");
const axios = require("axios");
const fs = require("fs");
const jwt = require("jsonwebtoken");
var cors = require("cors");

const app = express();
const port = 3000;

app.use(cors());

app.get("/", async (req, res) => {
  // This is the key file you downloaded from Apple
  var privateKey = fs.readFileSync("YOUR KEY FILE FROM DEVELOPER CENTER.p8");

  // Creating the signed token needs specific data
  var token = jwt.sign(
    {
      subject: "APP ID", // the reverse URL App Id you made above
    },
    privateKey,
    {
      jwtid: "YOUR TEAM ID.YOUR APP ID", // you can find your TeamID in the top right corner of your developer account, then put a `.` and then put in your reverse URL App Id
      issuer: "TEAM ID", // find your TeamID in your developer account
      expiresIn: "1h", // give it 1 hour of validity
      keyid: "KEY ID", // this is the ID for the key you created
      algorithm: "ES256", // this is the algorithm Apple used
      header: {
        // see details below for this
        id: "YOUR TEAM ID.YOUR APP ID",
      },
    }
  );

  // log your token so you can decode it and manually check it's ok, more below on this
  //console.log(token);

  const url =
    "https://weatherkit.apple.com/api/v1/weather/en/51.677612/-2.937941?dataSets=currentWeather&timezone=Europe/London";

  // add the token to your headers
  const config = {
    headers: { Authorization: `Bearer ${token}` },
  };

  // get the data
  const { data: weatherData } = await axios.get(url, config);

  // prove we have data
  //console.log(weatherData);

  // return the data to your front end
  res.json(weatherData);
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});
```

Your decoded JWT needs to look like this:

```javascript
{
  "header": {
    "alg": "ES256",
    "typ": "JWT",
    "kid": "KEY_ID",
    "id": "TEAM_ID.APP_ID" // this is the bit that has been tricking people up
  },
  "payload": {
    "subject": "APP_ID",
    "iat": 1654823790,
    "exp": 1654827390,
    "iss": "TEAM_ID",
    "jti": "TEAM_ID.APP_ID"
  },
  "signature": "d98jNki1tXX3mSYOAPp-Wpds8kEbGJ-lhKPtasdfSqoDyiJ2WbeI6U73UWCYWQgISlDOzaXdI5rSrSFcrg5g"
}
```

To get `id` in the header I needed to set it manually when creating the JWT, Apple is looking for `id` in the header and depending on your JWT signing library of choice that may not (probably won't) be there by default.

```javascript
 header: {
    id: "YOUR TEAM ID.YOUR APP ID", // see details below for this
},
```

## WeatherKit REST API Front end

To call the serve end point you can use the code below in an `index.html` to make a simple front end that starts with default data and then adds in the data from weather kit.

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.0.0-alpha.1/axios.min.js"
      integrity="sha512-xIPqqrfvUAc/Cspuj7Bq0UtHNo/5qkdyngx6Vwt+tmbvTLDszzXM0G6c91LXmGrRx8KEPulT+AfOOez+TeVylg=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
    ></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div class="container max-w-3xl mx-auto p-8">
      <h1 class="text-3xl text-center font-bold text-gray-700">All The Code</h1>
      <div
        class="shadow-lg shadow-black/40 w-[400px] p-4 mx-auto mt-10 rounded flex flex-col gap-y-6"
      >
        <h2 class="text-xl font-bold text-center">Current Weather</h2>
        <div class="text-7xl font-black text-center">
          <span id="temperature">0</span> <span class="font-thin">&#8451;</span>
        </div>
        <div class="flex flex-row justify-between">
          <div class="flex flex-col text-center w-1/3">
            <div id="wind" class="text-3xl font-bold">17.5</div>
            <div class="text-3xl">Wind</div>
          </div>
          <div class="flex flex-col text-center w-1/3">
            <div id="uv" class="text-3xl font-bold">0</div>
            <div class="text-3xl">UV</div>
          </div>
          <div class="flex flex-col text-center w-1/3">
            <div class="text-3xl font-bold"><span id="cloud">81</span>%</div>
            <div class="text-3xl">Cloud</div>
          </div>
        </div>
      </div>
      <div class="text-center mt-10">
        Weather data provided by
        <a href="https://weather-data.apple.com/legal-attribution.html"
          >Apple WeatherKit</a
        >
      </div>
    </div>
  </body>

  <script>
axios
  .get("http://localhost:3000", {
    headers: { "Access-Control-Allow-Origin": "*" },
  })
  .then((res) => {
    let temp = document.getElementById("temperature");

    temp.innerHTML = res.data.currentWeather.temperature;

    if (parseFloat(res.data.currentWeather.temperature) < 15) {
      temp.classList.add("text-blue-500");
    } else {
      temp.classList.add("text-red-500");
    }

    document.getElementById("wind").innerHTML =
      res.data.currentWeather.windSpeed;
    document.getElementById("uv").innerHTML = res.data.currentWeather.uvIndex;
    document.getElementById("cloud").innerHTML =
      res.data.currentWeather.cloudCover * 100;
  });
  </script>
</html>
```

When this page loads it will request weather data from Apple via your server with a sign JWT 😊

If you want to see this widget in action you can see it on [this page](https://allthecode.co/blog/post/setting-up-weatherkit-rest-api-in-node-js) on my site

If you're still learning to code and some, or all of this, doesn't make sense then head over to [All The Code](https://allthecode.co/) where I teach people how to be amazing web developers. Join my mailing list for a free coding quick start guide and weekly tip and advice.