Evaluation and tweaks using Strapi Headless CMS + GraphQL
Tags: javascript, headlesscms, react, webdev
Published: 2021-05-25 10:43:18

Recently we developed a career website. We faced the challenge to mitigate the risks of integrating legacy code while staying in scope, on budget and on time during project execution. Key requirements were the integrating of APIs such as Salesforce and XING. We had limited access to the previous developers, which increased risks due to potential unforeseen bottlenecks.

The approach we chose was to find a lightweight CMS backend with just enough functionality to match our specifications without being overly complex for our developers and our client (who maintains the website contents). 

From the various headless CMS options we compared (e.g. [Contentful](https://www.contentful.com/), [Prismic](https://prismic.io/), [Netlify CMS](https://www.netlifycms.org/) and [Kirby](https://getkirby.com/)) we identified, that Strapi is the most suitable for our case. [Strapi](https://strapi.io/) (a NodeJS Vue.JS based CMS) convinced is with its minimalistic UI and its simple integration with our React and GraphQL frontend tech stack. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2pmus5lp1lgbh2uujzo.png)

##What’s good about Strapi?
* minimal and clean UI
* very simple role and user management
* drag & drop builder for data types (collections), pages and modules
* intuitive content management
* GraphQL integration + playground (plugin needs to be installed in CMS admin panel)
* growing community and continuous development
* self-hosted CMS (great for data privacy!)

##Where is room for improvement?
* long initial installation process (using Docker containers)
* out-of-the-box Docker image was not ready to be easily integrated with Gitlab CI/CD and Ansible
* documentation a bit weary around JWT authentication process for users and content
* still early development with some bugs (e.g. our docker container needs to restart every time we make a change in the data model/ schema)

##Conclusion: How did Strapi work for our project?
The initial struggles to set up Strapi using Docker ultimately ended up saving us valuable time during the development of the website. We build our React components and could map them nicely with the CMS data schema (see in hacks below). Querying the data was easy using GraphQL. The Strapi + GraphQL playground made writing queries super simple while being able to debug a query live. In the end, this tech stack reduced the overall development time by almost 30%.

This freed up development time we then used to integrate and debug legacy code while staying on schedule. The existing Strapi REST API was well documented, so we built workflows to sync data between Salesforce APIs and our own database.

Our client likes Strapi because of its intuitive UI and as developers, we can enforce input restrictions such as limiting text lengths, required form fields and more to maintain content quality (and prevent breaking the designs).

## 3 tweaks when developing with Strapi
### (1) Remove query limit
Initially, the returned GraphQL queries are limited to 100 entries. This can be changed using a settings.json in */extenstions/graphql/config*. See more in the documentation.

```javascript
{
  "endpoint": "/graphql",
  "tracing": false,
  "shadowCRUD": true,
  "playgroundAlways": false,
  "depthLimit": 7,
  "amountLimit": 1000,
  "federation": false
}
```

### (2) Visualize Strapi markup as HTML
We used [React Markup](https://github.com/remarkjs/react-markdown) to visualize Rich Text contents. Some formatting like underlines needs to be handled manually, however. 

### (3) Coherent information architecture in front- and backend
We mapped the React component properties 1:1 to our Strapi data models. This creates consistency throughout the technologies:

**CMS Backend:**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kkhq039x15sxuqt1qw70.png)

**GraphQL queries:**

We also used GraphQL fragments to make code reusable and easily changed when the CMS schema changes.

```javascript
export const BUTTON_FRAGMENT = `
  title
  className
  fontColor
  buttonColor
  type
  route
`;
```

**React component with Typescript:**

```javascript
export type ButtonProps = {
  title: string,
  className?: string,
  fontColor?: string,
  buttonColor?: string,
  type?: string,
  onClick?: () => void,
  route?: string,
};

const Button = (props: ButtonProps): JSX.Element => {
  <ButtonEl className={`${props.className || ''} button`} onClick={props.onClick}>
  ...
  </ButtonEl>
};

export default Button;
 
```