I've recently created a portfolio website and needed a CMS to populate certain areas of my website.
I've been using Gridsome to generate a static website, so I wanted to hook that up to a headless CMS.
I chose SquidexCMS 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, 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.
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 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:
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:
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.
// 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 page as a PageQuery with 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
Top comments (0)