> ## Documentation Index
> Fetch the complete documentation index at: https://pulian.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# @hile/cache

> Build typed Redis read-through caches with TTL, tags, fieldable hashes, negative/stale options, and singleflight refreshes.

# @hile/cache

Build typed Redis read-through caches with TTL, tags, fieldable hashes, negative/stale options, and singleflight refreshes.

## Choose This Package When

| User asks for                | Use           | Also read                                                           |
| ---------------------------- | ------------- | ------------------------------------------------------------------- |
| Add read-through Redis cache | `@hile/cache` | `packages/infrastructure.md`, `recipes/redis-cache-singleflight.md` |

## Use When

Use these packages for database connections, Redis connections, structured logging, typed Redis caches, scheduled jobs, and file-system loaders.

## Do Not Use When

* Do not use default TypeORM or Redis services for multiple connections.
* Do not use `@hile/cache` without returning `new Cache(...)` from the loader function.
* Do not use `@hile/schedule` distributed mode without Redis lock TTLs sized for job duration.

## Install

```bash theme={null}
pnpm add @hile/typeorm @hile/ioredis @hile/logger @hile/cache @hile/schedule @hile/loader
```

## Imports

```ts theme={null}
import typeormService, { transaction } from '@hile/typeorm'
import redisService from '@hile/ioredis'
import { createLogger } from '@hile/logger'
import { Cache, defineCache, RedisCache } from '@hile/cache'
import { Scheduler, defineJob } from '@hile/schedule'
import { scanDirectory, compileRoutePath, toRouterPath, normalizePath, Loader } from '@hile/loader'
```

## Copy-Paste Example

```ts theme={null}
import { loadService } from '@hile/core'
import redisService from '@hile/ioredis'
import { Cache, defineCache, RedisCache } from '@hile/cache'

const userProfile = defineCache('user:{id:string}:profile', async ({ id }) => {
  const user = await loadUser(id)
  return new Cache(user).setExpire(300)
})

const redis = await loadService(redisService)
const cache = new RedisCache('app:', redis)
const users = await cache.loadCache(userProfile)
const user = await users.read({ id: 'u1' })
```

## More Examples

TypeORM transaction with compensation:

```ts theme={null}
await transaction(ds, async (runner, rollback) => {
  const user = await runner.manager.save(User, input)
  rollback(() => redis.del(`user:${user.id}`))
  await runner.manager.save(AuditLog, { userId: user.id, action: 'create' })
  return user
})
```

Distributed scheduled job:

```ts theme={null}
const scheduler = new Scheduler()
scheduler.add('daily-report', '0 8 * * *', async () => {
  await sendDailyReport()
}, {
  distributed: {
    redis,
    ttl: 60_000,
    namespace: 'reports',
    policy: 'skip-if-locked',
  },
})
```

## Runtime And Lifecycle Notes

* `Cache(undefined)` removes the key unless negative caching is configured.
* `Cache#setExpire(seconds)` uses seconds.
* `defineCache` typed placeholders support `string`, `number`, and `boolean`.
* `RedisCache.loadCache()` returns `read`, `write`, `remove`, `has`, and `multi`.
* `RedisCache.removeTag(tag)` removes tagged cache entries.
* `fieldable` caches use Redis hashes and cannot combine with stale or negative cache options.
* `Scheduler.add()` supports cron strings and `{ delay }`.
* `Scheduler.load()` reads default exports from `*.schedule.*` files produced by `defineJob()`.
* `scanDirectory()` matches `.ts`, `.js`, `.tsx`, `.jsx`, and `.mjs`.

## Anti-Patterns

* Returning raw values from `defineCache` handlers instead of `new Cache(value)`.
* Treating cache as source of truth.
* Forgetting to destroy manually created Redis or TypeORM clients.
* Scheduling jobs without idempotency when side effects can repeat.

## Verification Checklist

* Manual Redis clients call `disconnect()` during cleanup.
* Manual TypeORM data sources call `destroy()` during cleanup.
* Cache keys include app/tenant prefixes when shared Redis is used.
* Scheduled jobs have clear duplicate-run policy.

## Related Recipes

### Redis Cache With Singleflight

## Complete Example

```ts theme={null}
import { loadService } from '@hile/core'
import redisService from '@hile/ioredis'
import { Cache, defineCache, RedisCache } from '@hile/cache'

const userProfileCache = defineCache(
  'user:{id:string}:profile',
  async ({ id }) => {
    const profile = await loadProfileFromDatabase(id)
    if (!profile) return new Cache(undefined)
    return new Cache(profile).setExpire(300)
  },
  {
    singleflight: { ttl: 10_000, wait: 10_000 },
    stale: { ttl: 60 },
    negative: { ttl: 30 },
    tags: (params, data) => data ? [`user:${params.id}`] : [],
  },
)

const redis = await loadService(redisService)
const cache = new RedisCache('app:', redis)
const userProfiles = await cache.loadCache(userProfileCache)

const profile = await userProfiles.read({ id: 'u1' })
await userProfiles.remove({ id: 'u1' })
await cache.removeTag('user:u1')
```

## Package-Local AI Guide

This package also ships `AI.md` in npm so agents can read accurate examples after installation.
