---
title: Redis over HTTP
description: The same Redis store over fetch, for providers with only a REST API.
---

```ts server/orders.ts
import { createServer } from 'experimental-a2/server'
import { redisHttp } from 'experimental-a2/store-redis-http'
import { orders } from '@/contracts'

const url = process.env.UPSTASH_REDIS_REST_URL
const token = process.env.UPSTASH_REDIS_REST_TOKEN
if (!url || !token) throw new Error('set the Upstash REST URL and token')

export const ordersServer = createServer({
  contract: orders,
  store: redisHttp({ url, token }),
  handlers: {
    /* ... */
  },
})
```

Stores sessions exactly like [Redis](/stores/redis), with no client dependency
and nothing to set up. Use it when your provider only offers a REST API.

:::warning
Live streams trust the messages on the store's `:notify` channels. Browsers
never hold Redis credentials, so they can't reach those channels. The risk is a
credential that can publish but not write, such as a token another service
uses: it could add or hide events in live views. Grant publish access only to
credentials that can also write the store.
:::

## Options

| Option | |
| --- | --- |
| `url`, `token` | your provider's REST endpoint and token |
| `keyPrefix` | isolates several apps on one Redis. Default `'a2'` |
| `clock`, `ids` | test seams for timestamps and generated event ids |

## Live updates

- Live events and presence use Upstash's HTTP `SUBSCRIBE` endpoint, with the
  same URL and token. Each active channel holds one outbound HTTP subscription
  per store instance, shared by its viewers and closed when the last one
  leaves.
- Writes publish their wake-ups in the same Redis script as the append. A
  rejected notification doesn't fail the write.
- Idle streams re-read every ten seconds to recover a missed notification.
  Subscriptions reconnect on their own and catch up from the event cursor.
- After a reconnect, A2 re-reads presence and clears fields deleted during the
  gap, so presence may repeat its latest values.
- If `SUBSCRIBE` answers 404, 405, or 501, the store polls instead, every 25ms
  while busy and up to every two seconds when idle. Other errors stay errors.
