@fjall/payload

中文文档
39.2.2发布于 2026/11/15SEE LICENSE IN LICENSETypeScriptES Module近 30 天下载 2933

Fjall AWS adapters and utilities for Payload CMS

原始使用文档
文档目录14 节
  1. Installation
  2. Features
  3. Usage
  4. Database Adapter
  5. S3 Storage Plugin
  6. Environment Detection
  7. Conditional Static Directory
  8. Environment Variables
  9. Production (Lambda)
  10. Development
  11. Setting up and deploying a Payload project
  12. Configure the project
  13. Build and deploy
  14. Licence

@fjall/payload

AWS Lambda adapters for Payload CMS. This package provides drop-in replacements for Payload's database and storage adapters, optimised for serverless deployments on AWS.

Installation

npm install @fjall/payload @payloadcms/storage-s3

Features

  • fjallPostgresAdapter - PostgreSQL adapter with AWS Secrets Manager integration
  • awsS3Storage - S3 storage plugin for media uploads
  • fjallDefaults - Sensible CORS/CSRF defaults for Lambda
  • isProduction / isLambda - Environment detection helpers

Usage

Database Adapter

// payload.config.ts
import { buildConfig } from "payload";
import { fjallPostgresAdapter, fjallDefaults } from "@fjall/payload";
import { migrations } from "./migrations";

export default buildConfig({
  ...fjallDefaults(),
  db: await fjallPostgresAdapter({
    migrationDir: "./src/migrations",
    prodMigrations: migrations,
  }),
  // ... rest of config
});

The adapter automatically:

  • Fetches database credentials from AWS Secrets Manager in Lambda
  • Falls back to DATABASE_URL for local development
  • Configures SSL for Aurora PostgreSQL
  • Makes Payload's migration runs wait for each other, so environments that start together run each migration once. Each run holds Postgres advisory lock (1718250604, 1) on a connection of its own while it migrates, and a run that waits 15 minutes for it fails with an error that names the lock

S3 Storage Plugin

// plugins/index.ts
import { awsS3Storage } from "@fjall/payload";
import { Plugin } from "payload";

export const plugins: Plugin[] = [awsS3Storage()];

The plugin automatically:

  • Uses MEDIA_BUCKET_NAME environment variable set by Fjall infrastructure
  • Handles presigned URLs for uploads
  • Falls back to local filesystem in development

Environment Detection

import { isProduction, isLambda } from "@fjall/payload";

// Conditional logic based on environment
if (isProduction()) {
  // Running in AWS with Fjall infrastructure
}

if (isLambda()) {
  // Running in AWS Lambda runtime
}

Conditional Static Directory

For Media collections that need local uploads in dev but S3 in production:

import { isProduction } from "@fjall/payload";
import path from "path";

export const Media: CollectionConfig = {
  slug: "media",
  upload: {
    ...(isProduction()
      ? {}
      : {
          staticDir: path.resolve(__dirname, "../../public/media"),
        }),
  },
};

Environment Variables

Production (Lambda)

Set automatically by Fjall infrastructure:

VariableDescription
DATABASE_HOSTAurora PostgreSQL endpoint
DATABASE_PORTDatabase port (default: 5432)
DATABASE_NAMEDatabase name
DATABASE_SSLEnable SSL (true)
MEDIA_BUCKET_NAMES3 bucket for uploads

Credentials fetched from Secrets Manager:

  • DATABASE_USERNAME
  • DATABASE_PASSWORD

Development

VariableDescription
DATABASE_URLFull PostgreSQL connection string

Setting up and deploying a Payload project

Neither fjall create app nor fjall deploy sets a Payload project up for Lambda. fjall create app --name <app> --pattern payload --non-interactive writes fjall/<app>/infrastructure.ts, fjall/<app>/open-next.config.ts, the CDK project files beside them and the application's config file, and nothing changes your project's own files. In a terminal without --non-interactive, fjall create app opens the application wizard, which ignores --pattern: run fjall create and choose Patterns instead. Configure the project by hand, once, then build it at the project root and deploy what that build produced. The full recipe is on the Payload pattern page.

On Lambda, a site deployed this way needs fjall 39.0.13 or later constructs. The Payload pattern gives its server function the database user name and password, and PAYLOAD_SECRET, through Fjall's secrets support, and through fjall 39.0.12 that support does not deliver them. Read from the code, from 39.0.8 to 39.0.12 each start of the function stops in Fjall's secrets resolver before its handler runs, and before 39.0.8 a start could instead skip the resolver and start without the secrets. The cause is the version of AWS's Parameters and Secrets Lambda Extension that fjall 39.x pins, 1.0.103: measured on 15 October 2026 on arm64 in us-east-2, it answered each of 40 reads over the first 8 seconds of a function's start-up with a not-ready refusal, and, read from the code, the resolver treats that refusal as final. From 39.0.13 the resolver reads Systems Manager and Secrets Manager itself, so that no longer stops a Payload site whose function can reach the two services, and fjallPostgresAdapter takes the values the resolver handed over. No Payload site has been served end to end yet.

Configure the project

Work at the project root, the directory that holds package.json and the fjall/ folder. The commands below use npm.

  1. Install this package and the S3 storage adapter: npm install @fjall/payload @payloadcms/storage-s3.
  2. In payload.config.ts, use fjallPostgresAdapter with prodMigrations, and spread fjallDefaults() into buildConfig, as Database Adapter shows. Until the first migration exists, create src/migrations/index.ts holding export const migrations = []; so the import resolves, then generate the first migration with npx payload migrate:create before the first build.
  3. Add awsS3Storage() to the project's plugins, and keep staticDir out of production in the Media collection, as S3 Storage Plugin and Conditional Static Directory show.
  4. Guard every database query that runs during next build, as the Payload pattern page shows for the website template's pages: a generateStaticParams returns [] when isBuildTime() is true, and a page that queries as it renders exports dynamic = "force-dynamic".
  5. Before you build for AWS, comment out DATABASE_URL and NEXT_PUBLIC_SERVER_URL wherever .env or .env.production sets them, and wherever .env.local or .env.production.local does, which the build reads. Next.js copies .env and .env.production into its standalone output and OpenNext copies both into the server function (Next.js 16.3.8 and @opennextjs/aws 4.1.6, read), so every value in them ships with the deployed site.
  6. Check the project's Next.js major with npm ls next, then look for a buildCommand in the config object of fjall/<app>/open-next.config.ts: fjall 39.x writes that file without one, and later CLIs write buildCommand: "npm run build -- --webpack". On Next.js 16 the config needs that line, so add it if it is missing: Next.js 16 builds with Turbopack by default, and a Turbopack build refers to sharp by a hashed name that does not resolve on Lambda, so every route answers 500 (vercel/next.js issue 87737, open on 2026-09-28; measured on Next.js 16.3.3 with @opennextjs/aws 4.1.0). On Next.js 15, delete the line if it is there, and OpenNext runs npm run build: Next.js 15 builds with webpack already and refuses --webpack as an unknown option (Next.js 15.4.11, read).

Build and deploy

fjall deploy runs its build steps in fjall/<app>, which fails for an app laid out as fjall create app lays it out. At the project root:

  1. Install the OpenNext builder: npm install --save-dev @opennextjs/aws@^4.1.6. Do not build through npx open-next: where the builder is not installed, npx fetches open-next 3.1.3 from the registry instead, outside that range.

  2. Write the import map with the project's own Payload CLI: ./node_modules/.bin/payload generate:importmap.

  3. Build: ./node_modules/.bin/open-next build --config-path fjall/<app>/open-next.config.ts. Without --config-path, OpenNext reads an open-next.config.ts at the project root instead.

  4. Check that .open-next/server-functions/default/node_modules/sharp/package.json and .open-next/image-optimization-function/node_modules/sharp/package.json both say 0.35.5, with @img/sharp-linux-arm64 beside each. Then check that the build did not run with Turbopack: grep -rlE 'sharp-[0-9a-f]{8,}' .open-next/server-functions/default must print nothing. A file it prints refers to sharp by the hashed name a Turbopack build writes; set the build command and build again. Measured on 15 October 2026 with Next.js 16.3.8 and @opennextjs/aws 4.1.6 on a minimal application that imports sharp, the grep prints the server chunk of a Turbopack build and nothing for a webpack build; a Payload website-template bundle built with webpack on Next.js 15.4.10 prints nothing too.

    fjall 39.0.7 and later write the install block that puts sharp 0.35.5 into both functions. An app scaffolded by an earlier CLI keeps the block it was scaffolded with until you replace its sharpInstallConfig declaration with the one the Payload pattern page gives.

  5. Deploy: fjall deploy <app> --skip-build.

Rebuild with steps 2 to 4 before every deploy that changes the project's code or migrations: --skip-build deploys whatever .open-next holds.

Licence

Proprietary — see LICENSE.

版本与发布记录

@fjall/payload 共发布 179 个版本,首次发布于 2026/02/02;过去 12 个月发布了 179 个版本。

版本发布日期距今
39.2.2latest2026/11/15今天
39.2.12026/11/15今天
39.2.02026/11/124 天前
39.1.02026/11/114 天前
39.0.142026/11/114 天前
39.0.132026/11/087 天前

关于 @fjall/payload 的常见问题

@fjall/payload 是什么?

Fjall AWS adapters and utilities for Payload CMS

如何安装 @fjall/payload?

在项目目录运行 npm install @fjall/payload,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:payload(^3.0.0)、@payloadcms/db-postgres(^3.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@fjall/payload 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@fjall/payload 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@fjall/payload 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.3.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@fjall/payload 可以商用吗?

@fjall/payload 使用 SEE LICENSE IN LICENSE 许可。许可条款写在包内的独立文件中,请阅读该文件确认可否商用。以上为概括,不构成法律意见,请以许可原文为准。

@fjall/payload 的下载量有多少?

根据 npm 官方下载统计,@fjall/payload 在 2026/10/15 – 2026/11/13 这 30 天内被下载 2,933 次,日均约 98 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@fjall/payload 还在维护吗?

最新版本 39.2.2 发布于 2026/11/15(今天);至今共发布 179 个版本,其中过去 12 个月发布了 179 个;首次发布于 2026/02/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。