EDIT : I’ve noticed that some strings were replaced by placeholders inside the message, you’ll find the full solution here.
The problem
Make offers a Notion module called Update a Page Content which allows you to create as many blocks as you want.
The problem is that it cannot be used programmatically to add the same number of blocks as you have paragraphs in your content.
As a result, you usually end up with a single block containing all your content coming from another module, which is quite frustrating when you want to manipulate the content of your Notion page as usual, meaning one paragraph per block.
Solution 1 - Lazy (and expensive) mode
The first solution is to use the default Make modules to add as many blocks as there are paragraphs in your content.
Below is an example scenario: the first module contains just text in a JSON object that is converted to text from HTML with the Text parser
module. Nothing fancy here, and it can be whatever text you want to add to your Notion page coming from your modules.
The 3rd module
Set Variable
splits the content coming from Text Parser
to an array.
Then we add an iterator to iterates on each key of our
Paragraphs
array defined previously.
Then we take the value of the paragraphs array to create a block each time using the module
Append a Page Content
.
While this approach is simple and straightforward, you may notice that it can become very costly because creating a block here consumes one operation at a time ! So yes, if you have 30 carriage returns in your text, it will be 30 operations !
The advantage of this method is that it also allows you to style each block independently (bold, italic, etc.).
Solution 2 - API (and affordable) Mode
The previous solution is acceptable, but it becomes much too costly when dealing with hundreds of Notion pages or more. So, by reviewing the Notion API documentation, I found an expression that can perform the entire previous process in a single operation!
Simple scenario to update a Notion page with multiple paragraph in a SINGLE operation.
You’ll have to copy/paste the expression below inside the Body field of the Notion Module Make an API Call
and replace <your_text>
with the raw text coming from previous modules in your scenario.
{"children": [{{join(split(replace(replace(your_text; "/[\\n]{1,}/g"; "||"); "/[^|]+(?:\\|[^|]+)*/g"; "{""object"":""block"",""type"":""paragraph"",""paragraph"":{""rich_text"":[{""type"":""text"",""text"":{""content"":""$&""}}]}}"); "||"); ",")}}]}
For the module configuration, be careful with all the fields, which must match exactly as they are displayed on the right! All fields are mandatory !
URL
/v1/blocks/**<your_page_id>**/children
Not dashes inside <your_page_id>
!
Notion version
2022-06-28
Method
PATCH
(and not POST* ! )
- POST will throw an error even is everything else is correct !
Headers
Key : Content-type
Value : application/json
Beware to fill all the mandatory fields and the PATCH method, not POST !
Expression explained
You don’t have to understand the expression to use it, as long as you just replace your_text
with… your text ! But if you want to understand the logic behind it, read further…
So, let’s begin !
The logic behind it
To append a new block to an existing page, you have to send JSON to the Notion API “Append block children” endpoint, as formatted below, and use "object": "block"
for EACH paragraph.
{
"children": [
{
"object": "block",
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Your content"
}
}
]
}
}
]
}
The trick here is to perform a “mass” search-and-replace on each paragraph of your content to add the necessary JSON code for it to work, and generate a valid "object": "block"
for each paragraph.
Like nesting Russian dolls, we use many functions embedded within one another to create a valid JSON compatible with Notion, allowing us to add multiple blocks in just one API call!
I’ll start with the “doll” inside the expression so you can understand the process.
5️⃣{"children": [{{4️⃣join(3️⃣split(2️⃣replace(1️⃣replace(your_text; "/[\\n]{1,}/g"; "||"); "/[^|]+(?:\\|[^|]+)*/g"; "{""object"":""block"",""type"":""paragraph"",""paragraph"":{""rich_text"":[{""type"":""text"",""text"":{""content"":""DISCOURSE_PLACEHOLDER_19""}}]}}"); "||"); ",")}}]}
Prepare the content
We assume your content is raw text with paragraphs and no styling.
The replace()
function uses a regex expression [\\n]{1,}/g
to replace any carriage returns within the text with ||
(douple pipe) as a separator for each paragraph.
replace(
your_text;
"/[\\n]{1,}/g";
"||"
)
Surround all the paragraphs with Notion compatible JSON
Next, again with replace()
, we capture all the paragraphs separated with ||
with a regex, and include the necessary JSON in one line to generate one block per paragraph.
The regex /[^|]+(?:\\|[^|]+)*/g
is constructed so that it allows any text to include a pipe (which can sometimes happens), but detects ||
as a paragraph separator***.**
Inside the substitution regex, DISCOURSE_PLACEHOLDER_28
represents each time a paragraph is found.
This is by far the most complex step in this process !
replace(
replace(
your_text;
"/[\\n]{1,}/g";
"||");
"/[^|]+(?:\\|[^|]+)*/g";
"{""object"":""block"",""type"":""paragraph"",""paragraph"":{""rich_text"":[{""type"":""text"",""text"":{""content"":""DISCOURSE_PLACEHOLDER_29""}}]}}"
)
Generate and array with each blocks
Now, we transform all our paragraphs previously formatted with JSON inside an array with split()
, using the ||
as a separator**.**
split(
replace(
replace(
your_text;
"/[\\n]{1,}/g"; "||"
);
"/[^|]+(?:\\|[^|]+)*/g"; "{""object"":""block"",""type"":""paragraph"",""paragraph"":{""rich_text"":[{""type"":""text"",""text"":{""content"":""DISCOURSE_PLACEHOLDER_33""}}]}}"
);
"||"
)
Generate the final JSON
Using join()
, we assemble the final JSON from the previous array generated with split()
, using a comma “,” as a separator. This is to ensure that all blocks are separated with a comma, but no final comma is used !
join(
split(
replace(
replace(
your_text;
"/[\\n]{1,}/g"; "||"
);
"/[^|]+(?:\\|[^|]+)*/g"; "{""object"":""block"",""type"":""paragraph"",""paragraph"":{""rich_text"":[{""type"":""text"",""text"":{""content"":""DISCOURSE_PLACEHOLDER_36""}}]}}"
);
"||"
)
","
)
Final Steps
Finally, we surround our fancy expression with the remaining JSON elements.
{"children": [{
at the beginning]}
at the end.
{"children": [{{join(split(replace(replace(your_text; "/[\\n]{1,}/g"; "||"); "/[^|]+(?:\\|[^|]+)*/g"; "{""object"":""block"",""type"":""paragraph"",""paragraph"":{""rich_text"":[{""type"":""text"",""text"":{""content"":""DISCOURSE_PLACEHOLDER_39""}}]}}"); "||"); ",")}}]}
Now run your scenario, and your page will be updated with blocks for each paragraph in a single operation, instead of having one single large block of text!
Limitations
The expression works only for paragraph blocks inside Notion.
As you may have noticed, it is pretty straightforward to change the block type to something else, but the purpose of the expression is to append as many blocks as they are paragraphs from a text with ONE operation.
Also, styling is not supported (bold, italic, etc.)
Improvements
To my knowledge, it is not possible to modify the values of an array via a built-in function in Make, such as the expected JSON format by the API. None of the array functions allow you to do this directly in Make.
I would also be glad if someone could improve the expression and, if possible, find a way to retain styles from an HTML text.
I have a public Notion page with the same content here.