Home Migrate and Sync Craft CMS data in minutes with n8n
Post
Cancel

Migrate and Sync Craft CMS data in minutes with n8n

Using

  • Craft CMS
    • Built in Graphql explorer
    • Feed-Me Craft Plugin
  • n8n

We can move almost any data between environments, public or not, in just minutes.

Recently I had some tasks at work that inspired this.

Primarily - How do you sync entries between stage and prod, or stage and QA, when stage is behind an IP restricted firewall? In other words- I need to be on a vpn to use this staging environments control panel. So how the heck will the other environments get its data? Export/Import? Eew.

The Plan

There’s actually a few plans:

  1. Use graphiQl (note the i) to manually get some data and echo it to the other environments with n8n
  2. OR If we can publicly access the data source- then use graphql to access it directly and echo it to other environments with n8n Then,
  3. Use feed-me on the receiving end to digest the JSON feed
  4. Profit

Plan 1 (Indirect)


Go to your source environment, and go to the GraphiQl explorer (YourDomain/admin/graphiql). From here- use the ‘explorer’ to find the data you’re after.

A little about the GraphiQL explorer

Linkie - https://craftcms.com/docs/4.x/graphql.html#using-the-graphiql-ide

I’m sure you’re capable of reading the docs on this, but I’ll point out a few things:

Explorer side panel

By clicking the ‘Explorer’ button we can see our schema options in a click adventure:

01

In the Explorer we have a few adventure types- Query and Mutation. Using the bottom left dropdown we can start a new type in the ISE area:

02

The purple things are filters, the blue things are return values, the yellow are types

For example, here’s a query with a few common filters and return values for our greyhound org:

03

You can create what’s shown here with just clicking in the Explorer panel, followed by clicking the play button.

Query panel

You can also just start typing in the query panel, inside a valid query or mutation, and it will do a lot of work for you. For instance, CTRL+Space or CMD+Space gives you auto-complete options:

04

If you enter something that’s invalid, it generally just doesn’t show up in the results:

05

Refer to the documentation for more information and guidance: https://craftcms.com/docs/4.x/graphql.html#getting-started

Get this data into n8n

Plan 1, which is what I did at work recently in a pinch, is to simply copy the resulting JSON into n8n.

Start by placing a default code node: (The code in here is all getting removed)

06

Copy the raw output from your GraphiQL results, and modify the code node to return that data:

07

(It’s easiest to collapse the results, then select and copy them)

08

Now let’s hook this up to a webhook for processing elsewhere.

Webhook configuration

09

The webhook itself can be left as default if you like. A simple GET request to the URL shown in the webhook node will be enough to trigger this.

If you have a more complex set of requirements, such as to limit who/what can trigger this- then consider configuring the Authentication on the webhook, and/or using an IF node to inspect the webhook headers and/or IP address/domain the request came from to prevent accidental or unauthorized hits.

Now the response needs a little tweak, because if we run this it will only return the first item, by default:

10

So we’ll use a common expression to wrap up everything that’s incoming into a single response:

1
{{ JSON.stringify($input.all()) }}

The downside to this expression is it will give you the raw object from n8n which includes a couple wrapper elements:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[
    {
        "json": {      //             <----
            "id": "8494",
            "title": "Moe",
            "slug": "moe",
            "age": 2.8,
            "clickupId": "396kh10",
            "gdataLink": "https://www.greyhound-data.com/d?i=2481604"
        },
        "pairedItem": {//             <----
            "item": 0,
            "input": 0
        }
    },
    {
        "json": {      //             <----
            "id": "6868",
            "title": "Lucas",
            "slug": "lucas-3",
            "age": 2.5,
            "clickupId": "32rr46e",
            "gdataLink": "https://www.greyhound-data.com/d?i=2487522"
        },
        "pairedItem": {//             <----
            "item": 1,
            "input": 0
        }
    },
    {
        "json": {      //             <----
            "id": "6844",
            "title": "Mickey",
            "slug": "mickey",
            "age": 1.4,
            "clickupId": "35zpq6x",
            "gdataLink": null
        },
        "pairedItem": {//             <----
            "item": 2,
            "input": 0
        }
    }
]

To just grab the json keys we can use JMESPATH, in another common expression I use:

1
{{ JSON.stringify($jmespath($input.all(),"[].json")) }}

Which gives us a much nicer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
    {
        "id": "8494",
        "title": "Moe",
        "slug": "moe",
        "age": 2.8,
        "clickupId": "396kh10",
        "gdataLink": "https://www.greyhound-data.com/d?i=2481604"
    },
    {
        "id": "6868",
        "title": "Lucas",
        "slug": "lucas-3",
        "age": 2.5,
        "clickupId": "32rr46e",
        "gdataLink": "https://www.greyhound-data.com/d?i=2487522"
    },
    {
        "id": "6844",
        "title": "Mickey",
        "slug": "mickey",
        "age": 1.4,
        "clickupId": "35zpq6x",
        "gdataLink": null
    }
]

Ok great now we have a feed!

11

Test it before moving on

Go to the webhook node, and click on the test url to copy it:

12

Alternatively just active and save the workflow, and click on Production URL and copy that one. Production URLs only work when the workflow is active!

Click the Execute Workflow button, so it starts listening for the test call:

13

Visit the test URL, and note the response:

14

This is because we didn’t set the webhook to respond using the last node, so let’s fix that:

15

After selecting the Using Respond to Webhook Node setting, repeat the test.

Response in browser:

16

Great!

Save your workflow, and switch over to creating a new feed in feed-me.

Feed me config

Detailed, official instructions: https://docs.craftcms.com/feed-me/v4/guides/importing-entries.html#setup-your-feed

Quick version-

  • Go to your feed-me panel at yourCraftSite/admin/feed-me/feeds
  • Click New feed
  • For this demo, the configuration looks like this:

17

Click 'save and continue' after you’ve configured this how you like.

NOTE: This will now ping your URL so if it’s not active you need to have it listening!

It’s easiest to just active your workflow and use the production url during configuration

Assuming n8n responds, we move on to mapping the root element:

18

NOTE: I ran into an exception error here because my Feed-Me was a version behind, and the newest version solves an issue with custom sources. See: https://github.com/craftcms/feed-me/pull/1224

Save and Continue to begin field mapping, and use the nice interface to map your stuff:

19

Because this is a demo, I’ve mapped the Entity ID - and selected it as one of the unique fields to be sure I don’t clobber anything, and it only gets updated. Your map will probably NOT do this, so you can create new entries.

20

Save, continue, and click "Run Feed" if you’re ready.

Click over to the "Logs" tab in the feed-me menu, and review what it’s done:

21

That’s it!!

Plan 2 - Direct from another environment

Let’s say you’re copying/syncing production down to other environments. In that case we can skip the copy/paste stuff and just query Craft directly!

Follow the docs to make sure your graphql endpoint works and can be reached: https://craftcms.com/docs/4.x/graphql.html#getting-started

Now back in n8n let’s add a graphql call to mimic what we’d done in Plan 1:

22

Mind your credentials here- header auth will generally be in the format of Authorization: Bearer randomstring

eg:

23

24

The new flow looks like this- and will behave just as in Plan 1 but dynamically based on your query!

25

Conclusion

And THAT is how we can sync/migrate data in Craft CMS in mere minutes using n8n.

How cool is that?!

As always, I hope you found this interesting🙂

This post is licensed under CC BY 4.0 by the author.

Meili-Manager Update!

Extract GraphQL calls from a browser for debug in Postman

Comments powered by Disqus.