带有增强的空气质量应用
#aws #开源 #database #enhance

在北美的野火季节里,野火季节很好,最好关注当地的空气质量。
让我们从美国EPA的Airnow计划中获取一些实时数据。
即使API请求预算有限,我们也可以根据需要缓存和清爽数据获得活泼的结果。
所有功能都已经内置在增强中。

气动火

https://www.airnowapi.org/aq/observation/zipCode/current/?format=application/json&API_KEY=_SECRET_&zipCode=90210

这就是URL,我们将使用任何给定的邮政编码*。
要求空气质量指数(AQI) 如果在请求的邮政编码附近找到气象站,我们将获得一到三个测量的数组。

{
  AQI: 58,
  Category: {
    Name: 'Moderate',
    Number: 2,
  },
  DateObserved: '2023-06-08 ',
  HourObserved: 10,
  LocalTimeZone: 'MST',
  ParameterName: 'O3',
  ReportingArea: 'Denver-Boulder',
  StateCode: 'CO',
}

我们需要牢记that this API limits us to 500 requests per hour,但是文档也让我们知道大多数数据点每小时都会更新一次。
因此,如果我们的应用程序将其数据的副本缓存15分钟,我们可以每小时查询125个唯一的邮政编码 - 可能是一个良好的开始,至少直到它流行起来为止

*为简单起见,我坚持使用我们的数据,但是可以使用国际数据。
我建议检查Out IQAir

增强应用程序中的数据库

要缓存AQI信息,我们需要将数据存放在数据库中。它需要快速!

增强是建立在建筑师之上的,并免费提供所有弧线的超级大国。
这些功能(数据库访问,计划的功能,事件队列等)都是选择加入的,并且不膨胀您已部署的项目。

数据层由AWS的DynamoDB供电,因此设置并获取页面请求的数据非常快。
喜欢,<10ms快。

从数据库操作开始的最简单方法是安装 @begin/data(即使您部署到自己的AWS帐户,也可以与任何弧/增强应用一起使用此库)。

这里是用法的样本:

import data from '@begin/data'

const table = 'pizza'

await data.set([ // save multiple pizzas at once
  {table, key: 'bbq-chkn', toppings: ['chicken', 'chz', 'bbq sauce']},
  {table, key: 'southwest', toppings: ['chilis', 'red onion', 'corn', 'chz']},
  {table, key: 'hawaiian', toppings: ['chz', 'ham', 'pineapple']},
])

const bestPizza = data.get({table, key: 'hawaiian'})

console.log(bestPizza.toppings.at(-1)) // 'pineapple'

缓存API请求

我创建了一个简单的表单组件,该组件可以使用邮政编码参数获取 /美国路由。
然后,我的增强API函数将查找该邮政编码的最新AQI数据。
但是访客可以在一个小时的时间内刷新该页面几次,燃烧在我分配的Airnow API限制中。

AQI app form with single zip code field

因此,当第一次获取数据时,我会以15分钟的时间(TTL)值(TTL)值缓存 - AirNow的数据很少在给定位置更改多次一个小时。
然后,当该用户刷新或其他人请求相同的邮政编码时,我将首先检查数据库,并且仅查询API,如果该邮政编码不存在记录。

import data from '@begin/data'

const { AIRNOW_URL } = process.env
const table = 'aqi'

export async function get({ query: { zip } }) {
  if (!zip) return {} // just render the form

  const cacheKey = `zip:${zip}`

  let aqiData
  try {
    // first, check the cache
    const cached = await data.get({ table, key: cacheKey })

    if (cached?.aqiData) {
      aqiData = cached.aqiData // continue with data we have
    } else {
      // no cache, go get some data from AirNow
      const response = await fetch(AIRNOW_URL + `&zip=${zip}`)

      aqiData = await response.json()

      await data.set({ // cache the new data
        table,
        key: cacheKey,
        aqiData,
        ttl: Math.round(Date.now() / 1_000) + (60 * 15),
        // ↑ 15 minutes from now as seconds since epoch
      })
    }
  } catch (error) {
    return { status: 500 }
  }

  return { json: { aqiData } }
}

批准的没有太多的错误处理,但它是我的API层在这里多么简单的一个很好的例子。

现在,我们可以在由服务器渲染的自定义元素和浏览器本地Web组件供电的视图中介绍此数据!我会涉及创建SVG米的细节(受透气启发),但在这里我所做的工作:

Screenshot of the full AQI app interface

扩展示例

在我的完整示例中,我还根据其IP地址获取访问者可能的邮政编码,并提供由索引路线的结果。
AQI数据不仅缓存,而且邮政编码查找也是如此,因为该服务还具有每日限制。

交互式示例在这里: https://invent-k6b.begin.app/

和源代码: https://github.com/enhance-dev/enhance-example-aqi

您甚至可以看到仪表值的范围: https://invent-k6b.begin.app/test