2026-05-05 09:59:21 +03:00
# Next/image Provider
`image-platform` должен работать как custom image provider для `next/image` .
## Next.js contract
Next.js custom loader получает только:
- `src` ;
- `width` ;
- `quality` .
Поэтому public image URL должен принимать эти параметры напрямую и сам запускать read-through генерацию при miss.
2026-05-12 07:54:32 +03:00
## Remote source loader config
Remote source mode нужен для сценария, где consumer project уже имеет изображение в `public` или внешний image URL и хочет получить `srcset` без предварительной регистрации asset.
2026-05-05 09:59:21 +03:00
В Next.js приложении используется `loaderFile` :
```js
// next.config.js
module.exports = {
images: {
loader: "custom",
loaderFile: "./src/image-platform-loader.js",
2026-05-05 13:25:28 +03:00
qualities: [75, 80],
2026-05-05 09:59:21 +03:00
},
}
```
Пример loader:
```js
"use client"
2026-05-12 07:54:32 +03:00
import { createImagePlatformNextLoader } from "@image -platform/client"
2026-05-05 09:59:21 +03:00
2026-05-12 07:54:32 +03:00
export default createImagePlatformNextLoader({
baseUrl: process.env.NEXT_PUBLIC_IMAGE_PLATFORM_URL,
preset: "card",
project: process.env.NEXT_PUBLIC_IMAGE_PLATFORM_PROJECT,
sourceBaseUrl: process.env.NEXT_PUBLIC_SITE_ORIGIN,
})
2026-05-05 09:59:21 +03:00
```
Пример использования:
```tsx
import Image from "next/image"
export function ProductCard() {
2026-05-12 07:54:32 +03:00
return < Image src = "/images/product.jpg" width = {640} height = {420} alt = "Product" / >
2026-05-05 09:59:21 +03:00
}
```
2026-05-12 07:54:32 +03:00
Если `src` относительный, `sourceBaseUrl` превращает е г о в абсолютный source URL, например `https://site.example.com/images/product.jpg` . Если `src` уже абсолютный, он передаётся как есть.
2026-05-05 09:59:21 +03:00
## Public URL
2026-05-12 07:54:32 +03:00
Remote source URL:
```text
GET /p/{project}/remote/{preset}?src={sourceUrl}& w={width}& q={quality}& f=auto
```
Пример:
```text
https://img.example.com/p/acme/remote/card?src=https%3A%2F%2Fsite.example.com%2Fimages%2Fproduct.jpg& w=640& q=80& f=auto
```
Managed asset URL:
2026-05-05 09:59:21 +03:00
```text
GET /images/{assetId}/v{version}/{preset}?w={width}& q={quality}& f=auto
```
2026-05-05 13:25:28 +03:00
Route реализован в Fastify Gateway. Для `card` ширина должна входить в static preset allowlist: `320` , `640` , `960` .
2026-05-05 09:59:21 +03:00
Пример:
```text
https://img.example.com/images/asset_123/v4/card?w=640& q=80& f=auto
```
2026-05-12 07:54:32 +03:00
Для managed asset `src` не должен быть source URL. Это должен быть versioned platform identifier, например `asset_123/v4/card` . Source URL хранится в PostgreSQL и не раскрывается в public image URL.
2026-05-05 09:59:21 +03:00
`v{version}` меняется при обновлении source image. Старые URL можно кэшировать как immutable без purge.
2026-05-12 07:54:32 +03:00
Для remote source `src` является исходным URL. Этот режим не immutable по умолчанию: Gateway отдаёт `GATEWAY_REMOTE_CACHE_CONTROL` , потому что источник может быть mutable.
2026-05-05 09:59:21 +03:00
## Format auto
`f=auto` выбирает output format по `Accept` header:
1. `image/avif` , если клиент поддерживает AVIF и preset разрешает AVIF.
2. `image/webp` , если клиент поддерживает WebP и preset разрешает WebP.
3. `image/jpeg` или original fallback.
Для auto format обязательны headers:
```http
Vary: Accept
Cache-Control: public, max-age=31536000, immutable
Content-Type: image/avif | image/webp | image/jpeg
```
CDN и Gateway L1 cache должны учитывать `Vary: Accept` , иначе можно отдать AVIF клиенту без AVIF support.
## Read-through behavior
```text
client -> CDN -> Fastify gateway -> L1 memory -> Backend -> RabbitMQ -> Worker -> imgproxy -> S3
```
Поведение:
- CDN HIT: backend не вызывается.
- Gateway L1 HIT: backend не вызывается.
- Gateway L1 MISS: Gateway вызывает Backend internal ensure endpoint.
- S3 HIT: Backend отдаёт bytes Gateway, Gateway кладёт result в L1.
- S3 MISS: Backend ставит RabbitMQ job, Worker генерирует variant через external imgproxy, сохраняет в S3, обновляет PostgreSQL, Backend возвращает bytes Gateway.
Так достигается Cloudinary-like поведение: первый запрос создаёт derived asset, следующие запросы отдаются из cache/storage.