SquidexCMS to Gridsome using Content API
Tags: javascript, gridsome, squidex, webdev
Published: 2021-03-23 08:40:44

I've recently created a portfolio website and needed a CMS to populate certain areas of my website. 

I've been using [Gridsome](https://gridsome.org) to generate a static website, so I wanted to hook that up to a headless CMS. 

I chose [SquidexCMS](https://squidex.io) for the starter free pricing:  
- 20.000 API Calls per Month
- 2 GB Traffic per Month
- 500 MB Storage
- 2 Contributors

This enabled me to use it for a basic portfolio website, as the stats above covered me. 

It was quite easy to setup SquidexCMS, using schemas and content and it didn't take too long to populate with my content (I'll write this up in another post soon). 

Next I needed to connect Squidex to my local website. In Gridsome you can do this within the gridsome.server.js file [docs](https://gridsome.org/docs/fetching-data/), which is located in the root of the solution. 

This is my solution to connecting to the Content API used within Squidex and Gridsome JS to add GraphQL collections. 

[gridsome.server.js](https://github.com/garpunkal/garpunkal.dev/blob/main/gridsome.server.js)

```javascript
const axios = require('axios');

module.exports = function (api) {

  api.loadSource(async actions => {

    // config
    const baseApiUrl = 'https://cloud.squidex.io/api/content/garpunkaldev/';
    const config = {
      headers: {
        "X-Flatten": true,
        "X-NoResolveLanguages": 1,
        "X-Languages": "en",
        "timeout": 1000,
        "retry": 3,
        "retryDelay": 4000
      }
    };

    // gather data from api
    const { data: companyData } = await GetAsync(baseApiUrl + 'company', config);
    const { data: projectData } = await GetAsync(baseApiUrl + 'project', config);
    const { data: experienceData } = await GetAsync(baseApiUrl + 'experience', config);
    const { data: homeData } = await GetAsync(baseApiUrl + 'home', config);

    // experiences    
    const expCollection = actions.addCollection({ typeName: 'Experiences' })
    for (const item of experienceData.items) {

      // filter relations   
      const company = companyData.items.find(function (x) { return x.id === item.data.company[0] });
      const projects = BuildList(item.data.projects, projectData.items);
      const contribs = BuildList(item.data.contributions, projectData.items);
      // map
      expCollection.addNode(MapExperience(item, company, projects, contribs))
    }

    // highlights
    const highCollection = actions.addCollection({ typeName: 'Highlights' })
    for (const item of projectData.items) {
      // map
      if (item.data.IsHighlight === true) {
        highCollection.addNode(MapProject(item));
      }
    }

    // home
    const homeCollection = actions.addCollection({ typeName: 'Homes' })
    for (const item of homeData.items) {
      homeCollection.addNode(MapHome(item));
    } 
  })

  async function GetAsync(url, config) {
    return await axios
      .get(url, config)
      .then(response => { return response; })
      .catch(err => { console.log(err); });
  }

  function BuildList(selection, source) {
    var items = [];

    if (selection)
      selection.forEach(function (item) {
        const found = source.find(x => x.id == item);
        if (found != null)
          items.push(found);
      });

    return items;
  }

  function MapProject(item) {
    return {
      "id": item.data.id,
      "title": item.data.title,
      "position": item.data.position,
      "url": item.data.url,
      "image": {
        "webp": "https://cloud.squidex.io/api/assets/garpunkaldev/" + item.data.image[0] + "?cache=31536000&format=WEBP",
        "url": "https://cloud.squidex.io/api/assets/garpunkaldev/" + item.data.image[0] + "?cache=31536000",
        "alt": item.data.title
      },
      "sortOrder": item.data.SortOrder ?? 0,
      "isHighlight": GetBool(item.data.IsHighlight),
      "isWinner": GetBool(item.data.IsWinner)
    }
  }

  function MapExperience(item, company, projects, contribs) {
    return {
      "id": item.data.id,
      "title": company.data.title,
      "job": item.data.job,
      "location": item.data.location,
      "logo": {
        "webp": "https://cloud.squidex.io/api/assets/garpunkaldev/" + company.data.logo[0] + "?cache=31536000&format=WEBP",
        "url": "https://cloud.squidex.io/api/assets/garpunkaldev/" + company.data.logo[0] + "?cache=31536000",
        "background": company.data.logoBackgroundColour,
        "alt": company.data.title
      },
      "url": company.data.url,
      "shortUrl": company.data.shortUrl,
      "from": GetDate(item.data.from),
      "to": GetDate(item.data.to),
      "isCurrent": GetBool(item.data.isCurrent),
      "description": item.data.description,
      "hideDescription": GetBool(item.data.hideDescription),
      "projects": {
        "title": item.data.projectsLabel,
        "items": projects.map(p => ({ "name": p.data.title, "url": p.data.url }))
      },
      "contributions": {
        "title": item.data.contributionsLabel,
        "items": contribs.map(p => ({ "name": p.data.title, "url": p.data.url }))
      },
      "orderDate": item.data.from
    }
  }

  function MapHome(item) {
    return {
      "id": item.id,
      "title": item.data.title,
      "subTitle": item.data.subTitle,
      "githubUrl": item.data.githubUrl,
      "image": {
        "webp": "https://cloud.squidex.io/api/assets/garpunkaldev/" + item.data.profileImage[0] + "?cache=31536000&format=WEBP",
        "url": "https://cloud.squidex.io/api/assets/garpunkaldev/" + item.data.profileImage[0] + "?cache=31536000",
        "alt": item.data.title
      },
      githubSource: MapLink(item.data.githubSource),
      specialisms: item.data.specialisms.map(p => MapLink(p)),
      socials: item.data.socials.map(p => MapLink(p)),
      footerLinks: item.data.footerLinks.map(p => MapLink(p)),
      projectsLabel: item.data.projectsLabel,
      articlesLabel: item.data.articlesLabel,
      experiencesLabel: item.data.experiencesLabel,
    };
  }

  function MapLink(item) {
    return {
      "title": item.title ?? "",
      "url": item.url ?? "",
      "cssClasses": item.cssClasses?? "",
      "svgPath": item.svgPath?? "",
      "svgStroke": item.svgStroke?? "",
      "svgStrokeWidth": item.svgStrokeWidth?? "",
      "svgFill": item.svgFill?? "",
      "svgStrokeLineCap": item.svgStrokeLineCap?? "",
      "svgStrokeLineJoin": item.svgStrokeLineJoin?? ""
    }
  }
```


I use [Axios](https://github.com/axios/axios) to connect to Squidex and pull the response. 

It's easier to consume the Squidex content source for single language based content, if you use these headers: 

```javascript
const config = {
      headers: {
        "X-Flatten": true,
        "X-NoResolveLanguages": 1,
        "X-Languages": "en",
        "timeout": 1000,
        "retry": 3,
        "retryDelay": 4000
      }
    };
```

This will flatten the output so you don't need to be concerned about multilingual content. 

I then pulled in all of the sources at once to reduce API calls, as you're limited on these within the starter plan: 

```javascript
    const { data: companyData } = await GetAsync(baseApiUrl + 'company', config);
    const { data: projectData } = await GetAsync(baseApiUrl + 'project', config);
    const { data: experienceData } = await GetAsync(baseApiUrl + 'experience', config);
    const { data: homeData } = await GetAsync(baseApiUrl + 'home', config);

```

Once I had the data locally, I could filter this based on my requirements. 

```javascript
 // experiences    
    const expCollection = actions.addCollection({ typeName: 'Experiences' })
    for (const item of experienceData.items) {

      // filter relations   
      const company = companyData.items.find(function (x) { return x.id === item.data.company[0] });
      const projects = BuildList(item.data.projects, projectData.items);
      const contribs = BuildList(item.data.contributions, projectData.items);
      // map
      expCollection.addNode(MapExperience(item, company, projects, contribs))
    }

    // highlights
    const highCollection = actions.addCollection({ typeName: 'Highlights' })
    for (const item of projectData.items) {
      // map
      if (item.data.IsHighlight === true) {
        highCollection.addNode(MapProject(item));
      }
    }

    // home
    const homeCollection = actions.addCollection({ typeName: 'Homes' })
    for (const item of homeData.items) {
      homeCollection.addNode(MapHome(item));
    } 
```

I filtered the contents down, building up a list and then adding to a collection. 

These collections would then be used on the [Index.vue](https://github.com/garpunkal/garpunkal.dev/blob/main/src/pages/Index.vue) page as a PageQuery with GraphQL.

```graphql

query {
   homes: allHomes(limit: 1) {        
    edges { 
      node {
        id
        title
        subTitle
        githubUrl       
        image {
          webp
          url
          alt
        }
        githubSource {
          title
          url
          svgPath
        }
        socials { 
          title         
          url
          cssClasses
          svgPath
          svgStroke
          svgStrokeWidth
          svgFill
          svgStrokeLineCap
          svgStrokeLineJoin
        }
        specialisms {           
          title         
          url
          cssClasses
          svgPath
          svgStroke
          svgStrokeWidth
          svgFill
          svgStrokeLineCap
          svgStrokeLineJoin
        }
        footerLinks { 
          title         
          url
          cssClasses
          svgPath
          svgStroke
          svgStrokeWidth
          svgFill
          svgStrokeLineCap
          svgStrokeLineJoin
        }
        projectsLabel
        articlesLabel
        experiencesLabel
        }
      } 
    }
    articles: allDevToArticles(page:0, perPage: 11, sortBy: "published_at", order: DESC) {
      edges {
        node {
          id
          title
          published_at        
          description
          tag_list
          canonical_url
          cover_image
          positive_reactions_count
          comments_count         
        }
      }
    }  
   experiences: allExperiences(sortBy: "orderDate", order: DESC) {
    edges {
      node {
        id
        title
        job
        location       
        logo {
          webp
          url			   
	  background
	  alt
        }
        url
        shortUrl  
        from
        to
        isCurrent
        hideDescription
        description
        projects {
          title
          items {
            name
            url
          }
        }       
        contributions {
           title
          items {
            name
            url
          }
        }
        orderDate
      }
    }
   }
  highlights : allHighlights(sort: [{ by: "sortOrder", order: DESC }, { by: "title", order: ASC }]) {
    edges {
      node {
        id        
        title
        image {
          webp
          url
          alt         
        }
        url
        position        
        sortOrder
        isHighlight
        isWinner       
      }
    }
  }  
}
```

This was a real quick example of how to import data into Gridsome from Squidex. 

I hope it helps, and if you have any questions, please feel free to ask! :) 

Thanks