Payload Adsign Plugin

External Relationship Field

Relate to records in an external, non-Payload source

A relationship-style field for records that live in an external, non-Payload source (another headless CMS, a SaaS product, a partner API, …). Renders as a Payload-native async combobox with debounced search, infinite scroll, and label resolution for existing values.

Usage

1. Define a source

One source per file. Each exports a { handler, externalLink? } config.

externalRelationshipSources/tenants.ts
import type { ExternalRelationshipSourceConfig, ExternalRelationshipSourceHandler } from '@adsign/payload-adsign-plugin';
import { stringify } from 'qs-esm';

const handler: ExternalRelationshipSourceHandler = async ({ search, values, page = 1 }) => {
    const query = stringify(
        {
            where: {
                ...(search && { name: { like: search } }),
                ...(values?.length && { id: { in: values } }),
            },
            page,
            limit: 20,
        },
        { addQueryPrefix: true },
    );

    const res = await fetch(`${process.env.MASTER_URL}/api/tenants${query}`, {
        headers: { Authorization: `Bearer ${process.env.MASTER_TOKEN}` },
    });
    const { docs, hasNextPage } = await res.json();

    return {
        options: docs.map((d: { id: string; name: string }) => ({ label: d.name, value: d.id })),
        hasMore: hasNextPage,
    };
};

export const Tenants: ExternalRelationshipSourceConfig = {
    slug: 'tenants',
    handler,
    externalLink: {
        label: 'Open in Master',
        url: 'https://master.example.com/admin/collections/tenants',
    },
};

2. Register it in plugin config

payload.config.ts
import { adsignPlugin } from '@adsign/payload-adsign-plugin';
import { Tenants } from './externalRelationshipSources/tenants';

adsignPlugin({
    siteName: 'My Site',
    domain: 'example.com',
    externalRelationshipSources: [Tenants],
})

3. Run type generation

pnpm payload generate:types

4. Use it in any collection

import { externalRelationshipField } from '@adsign/payload-adsign-plugin';

export const Pages: CollectionConfig = {
    fields: [
        externalRelationshipField({
            name: 'tenants',
            hasMany: true,
            source: 'tenants',
        }),
    ],
};

Handler contract

ArgTypePurpose
searchstring?Debounced (300ms) user input for filtering
valuesstring[]?Resolve labels for existing values on mount
pagenumber?1-based page for infinite scroll
reqPayloadRequestAccess to req.user, req.payload, etc.

Returns:

{
    options: { label: string; value: string }[];
    hasMore?: boolean;
}

Field options

OptionTypeDescription
namestringField name (required)
sourceExternalRelationshipSourceSlugSlug of a registered source
hasManybooleanStore an array of values instead of a single one
...TextFieldAll other text-field options are passed through

Source options

OptionTypeDescription
slugstringUnique slug — referenced by externalRelationshipField({ source })
handlerExternalRelationshipSourceHandlerServer-side function that returns options
externalLink{ label, url }Optional link rendered beside every field bound to this source

On this page