This is an example for Payload CMS and Clerk integration.
Part 2 - Advanced integration
https://www.youtube.com/watch?v=egKaeOuddFA
Source code for the video is in the part-2 branch: https://github.com/DanailMinchev/payload-clerk-example/tree/feat/part-2
Part 1 - Basic integration
https://www.youtube.com/watch?v=7PNGNqqFlu0
Source code for the video is in the part-1 branch: https://github.com/DanailMinchev/payload-clerk-example/tree/feat/part-1
Install dependencies:
npm ci- Create a new Clerk application and configure:
Enable Email as Sign in option.
The setup is described in details in the videos above, but here are the settings for reference:
{
"metadata": "{{user.public_metadata}}"
}-
Copy the
env.examplefile into.env.localfile. -
Set Clerk environment variables documentation:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
Your Clerk app's Publishable Key, which you can find in the Clerk Dashboard. It will be prefixed with pktest in development instances and pklive in production instances.
CLERK_SECRET_KEY
Your Clerk app's Secret Key, which you can find in the Clerk Dashboard. It will be prefixed with sktest in development instances and sklive in production instances. Do not expose this on the frontend with a public environment variable.
SIGNING_SECRET
In case you are going to use Webhooks, you will need to set Signing Secret.
- Set Payload environment variables documentation
PAYLOAD_SECRET
This environmental variable acts as your secret key. It's paramount that you ensure its value is both secure and strong, as it's integral to the encryption and decryption process.
DATABASE_URI
This is the database connection string. Uncomment DATABASE_URI for SQLite or PostgreSQL.
- If you selected the
PostgreSQLyou can use Docker:
docker compose upand edit the src/payload.config.ts file:
Uncomment:
import { postgresAdapter } from "@payloadcms/db-postgres";Comment / delete:
import { sqliteAdapter } from "@payloadcms/db-sqlite";Uncomment:
// PostgreSQL
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI || "",
},
}),Comment / delete:
// SQLite
db: sqliteAdapter({
client: {
url: process.env.DATABASE_URI || "",
},
}),- For testing and local development purposes following users are being used:
- all-roles-1+clerk_test@example.com
- super-admin-1+clerk_test@example.com
- admin-1+clerk_test@example.com
- editor-1+clerk_test@example.com
- user-1+clerk_test@example.com
Please set the environment variables accordingly with their passwords in your .env.local file:
E2E_CLERK_ALL_ROLES_USER_EMAIL=all-roles-1+clerk_test@example.com
E2E_CLERK_ALL_ROLES_USER_PASSWORD=
E2E_CLERK_ALL_ROLES_USER_PHONE=+19735550101
E2E_CLERK_SUPER_ADMIN_USER_EMAIL=super-admin-1+clerk_test@example.com
E2E_CLERK_SUPER_ADMIN_USER_PASSWORD=
E2E_CLERK_SUPER_ADMIN_USER_PHONE=+19735550102
E2E_CLERK_ADMIN_USER_EMAIL=admin-1+clerk_test@example.com
E2E_CLERK_ADMIN_USER_PASSWORD=
E2E_CLERK_ADMIN_USER_PHONE=+19735550103
E2E_CLERK_EDITOR_USER_EMAIL=editor-1+clerk_test@example.com
E2E_CLERK_EDITOR_USER_PASSWORD=
E2E_CLERK_EDITOR_USER_PHONE=+19735550104
E2E_CLERK_AUTHENTICATED_USER_EMAIL=user-1+clerk_test@example.com
E2E_CLERK_AUTHENTICATED_USER_PASSWORD=
E2E_CLERK_AUTHENTICATED_USER_PHONE=+19735550105The above users are using test emails and test phone numbers as described in Test emails and phones.
If you want to use phone numbers, you need to enable "Phone number" in the Clerk's dashboard, otherwise seed endpoint will not work.
Otherwise, leave the E2E_CLERK_*_USER_PHONE environment variables empty.
- Register the
E2Eusers.
You can register the E2E users from the above point manually or automatically using the GET /api/app/seed endpoint.
Run the application: npm run dev
Registering automatically
Invoke / navigate to http://localhost:3000/api/app/seed endpoint.
Observe the console for logs.
WARNING: the endpoint will delete your existing data
Registering manually
You should register your super-admin-1+clerk_test@example.com user in Clerk manually and set the super-admin role.
The super-admin-1+clerk_test@example.com user should have following public metadata:
{
"roles": ["super-admin"]
}and you should set the rest of the user roles via the admin dashboard as follows:
- all-roles-1+clerk_test@example.com (super-admin role, admin role, editor role)
- admin-1+clerk_test@example.com (admin role)
- editor-1+clerk_test@example.com (editor role)
- user-1+clerk_test@example.com (no role, no changes to user's metadata, used for simulating a website user / registered user)
- Run the development server (if not running already):
npm run devOpen http://localhost:3000 with your browser to see the result.
Please refer to the official documentation - Sync Clerk data to your app with webhooks.
The app exposes POST /api/clerk/webhooks endpoint which can be configured in
the Clerk Dashboard.
Environment variables:
SIGNING_SECRET: "Signing Secret" from the Clerk Dashboard.
Currently, the webhooks endpoint is listening on the following events:
user.createduser.updateduser.deleted
Please follow the official documentation - Setup & Installation
To run ngrok in Docker (macOS):
docker run -it -e NGROK_AUTHTOKEN={NGROK_AUTHTOKEN} ngrok/ngrok:latest http host.docker.internal:3000 --url={NGROK_DOMAIN}To run ngrok in Docker (other OS):
docker run -it -e NGROK_AUTHTOKEN={NGROK_AUTHTOKEN} ngrok/ngrok:latest http 3000 --url={NGROK_DOMAIN}To run ngrok using native binary:
ngrok http 3000 --url={NGROK_DOMAIN}Replace {NGROK_AUTHTOKEN} with your authtoken.
Replace {NGROK_DOMAIN} with your free static domain,
for example: your-static-domain-here.ngrok-free.app.
There are E2E tests implemented using Playwright.
They are devided into two categories
- api-tests Used to verify the access control.
- app-tests Used to verify the app pages, including those behind authentication.
Before running the tests, you should install dependencies by executing:
npx playwright install
npx playwright install-depsThen you can use following scripts:
-
For UI mode:
npm run playwright:test:ui
-
For command line mode with debugging:
npm run playwright:test:debug
-
For command line mode (used for CI):
npm run playwright:test




