Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

simple solution: modify value of relationship field within afterRead hook

default discord avatar
unonweblast year
2 2

Hi,

as there are some threads about the relationship & afterRead hook matter I'd like to share my findings:

Related Threads:

The relationship field:

  • stores relationTo: 'collection-slug' and value: 'collection-id'
  • is populated after the afterRead hook

The afterRead hook...

  • is called both by the admin UI and server side functions
  • is called before relationships are populated
  • may return a modified result

If we want to modify the value of the relationship field when it's queried (which means in the afterRead hook), we face the following issue:
We need to include the original relationship data when the admin UI reads the field (otherwise it breaks).
But if we include relationTo and value properties, the relationship field is going to be populated automatically.

The solutions is:

  1. Set maxDepth: 0 which practically disables the automatic population of the field.
  2. Populate the field manually whithin a afterRead hook
const relationshipField = {
	type: 'relationship',
	name: 'internal',
	label: 'Document to link to',
	relationTo: ['pages'],
	required: true,
	maxDepth: 0, // <-- important
	hooks: {
		afterRead: [
			(args) => extractFieldsFromRelationship(args, ['url'])
		]
	}
}

async function extractFieldsFromRelationship(args, topLevelFields = []) {
	// afterRead

	if (typeof args.value === 'undefined') return

	const refCollection = args.value.relationTo
	const refID = args.value.value

	const doc = await payload.findByID({
		collection: refCollection,
		id: refID,
		depth: 1,
	})
	
	const extract = {}

	topLevelFields.map(field => {
		if (doc[field]) extract[field] = doc[field]
	})

	// include this - otherwise the admin UI will not work properly
	extract.relationTo = refCollection 
	extract.value = refID

	//return doc // This is the default way. The whole doc is included.
	return extract
}
  • Selected Answer
    discord user avatar
    jacobsfletch
    last year

    @unonweb this is great!! The only thing that jumps out at me are that it seems like you should not have to set maxDepth: 0 — because this requires you to make that extra query from within your hook. The entire document is already present in the args, so couldn't you just strip out the irrelevant fields without querying your relationship again?

    That first discussion you linked has some pretty good information, for others who stumble by. Namely, doing this will break GraphQL, and that this is really where GraphQL shines.

    Thank you for this thorough example!

  • default discord avatar
    christian-reichartlast year

    Thanks for that tip!
    Also related, solves that really elegantly: #1288

    I think using REST has a lot of benefits over GraphQL for certain cases, but it's currently handicapped by the relationships because of this select issue, also when using an internal link inside the Rich Text for example.

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team..