DEV Community

Cover image for Evaluation and tweaks using Strapi Headless CMS + GraphQL
Sascha Bardua for Taikonauten

Posted on • Updated on

Evaluation and tweaks using Strapi Headless CMS + GraphQL

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, Prismic, Netlify CMS and Kirby) we identified, that Strapi is the most suitable for our case. Strapi (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

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.

  "endpoint": "/graphql",
  "tracing": false,
  "shadowCRUD": true,
  "playgroundAlways": false,
  "depthLimit": 7,
  "amountLimit": 1000,
  "federation": false
Enter fullscreen mode Exit fullscreen mode

(2) Visualize Strapi markup as HTML

We used React Markup 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

GraphQL queries:

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

export const BUTTON_FRAGMENT = `
Enter fullscreen mode Exit fullscreen mode

React component with Typescript:

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}>

export default Button;

Enter fullscreen mode Exit fullscreen mode

Top comments (0)