Qwik x caisy

If you want to start with a complete project, you can find the starting template here:

Prerequisites

In this example, we build on the blog article created in the Quick Start. If you haven't completed it yet, it is recommended to go through the first four steps of the Quick Start once more.


Qwik Prerequisites

You could use regular JavaScript for this example, but we suggest using TypeScript to fully take advantage of the type safety and autocompletion features offered by a GraphQL API.

To proceed, the following query should retrieve the article data using the slug of the previously created article when running in your playground and setting the right slug in the variables.

query allBlogArticle($slug: String) {
  allBlogArticle(where: { slug: { eq: $slug } }) {
    edges {
      node {
        text {
          json
        }
        title
        slug
        id
      }
    }
  }
}

In this example, you will need your project ID and a valid API key.


Create Qwik App

To create a new Qwik project, with the Qwik CLI, which generates a blank starter so that you can quickly familiarize yourself with it.
Qwik supports NPM, yarn, and pnpm. Choose the package manager you prefer and run the following command:

npm create qwik@latest
pnpm create qwik@latest
yarn create qwik

The CLI guides you through an interactive menu to set the project-name, select one of the starters, and asks if you want to install the dependencies. Find out more about the files generated by referring to the Project Structure documentation.

To start the development server run:

npm start
pnpm start
yarn start

First page

pages are created by adding a new index.tsx file in the src/routes directory. Pages are exported default Qwik component, which will be rendered as the content of the page.

import { component$ } from '@builder.io/qwik';
 
// Notice the default export
export default component$(() => {
  return <a href="/blog/my-first-article">a link to our first blog page</a>;
});

Note: Replace my-first-article with the slug you have set in the quick start


If the development server is running, you should see a link on your index page. However, clicking the link will result in a 404 page. Let's fix this by creating a dynamic routed page that handles all of our blog pages.
To do this, create a folder called [...slug] in the path src/routes/blog/. In this file, you can add a simple "Hello World" placeholder like this:

src/routes/blog/[...slug]/index.tsx
import { component$ } from "@builder.io/qwik";

export default component$(() => <h1>Hello World!</h1>);


If you visit your site with the link on the index page, you will see your hardcoded "Hello World" headline. Let's make that text dynamic by fetching it from caisy in the next step.

website outline with example website "hello world"

Data fetching

You can install GraphQL-request by running the following command in your terminal or command prompt:

npm install graphql-request graphql --save


For proper interaction with the external API, the environment variables CAISY_API_KEY and CAISY_PROJECT_ID must be defined in a .env file.
If you need information on where to find this in your project, see:


The .env file may look like this:

.env
CAISY_PROJECT_ID=54026eaa-a8fc-49f9-ac37-ebe5148c4926
CAISY_API_KEY=Q1Bi5yudIr4HxQEtVt2fmalZRyndjuDW


The query used is from the Quickstart example and can be tested in the Playground.

Picture of the code on the caisy playground

We will use route loaders, to load this data in the server and then render it in the component.

Qwik routeLoader$()

Route Loaders load data in the server so it becomes available to use inside Qwik Components. They trigger when SPA/MPA navigation happens so they can be invoked by Qwik Components during rendering.

export const useFetchSlug = routeLoader$(async (requestEvent) => {
  const slug = requestEvent.params.slug as string;
  return slug;
});

export default component$(() => {
  const slug = useFetchSlug();
  return <h1>{slug.value}</h1>;
});

Route Loaders can only be declared inside the src/routes folder, in a layout.tsx or index.tsx file, and they MUST be exported.


After using the GraphQL query from the playground on the page with GraphQL request your code might look like this:

src/pages/blog/[...slug]/index.tsx
import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
import { GraphQLClient, gql } from "graphql-request";

export const useFetchData = routeLoader$(async (requestEvent) => {
  const slug = requestEvent.params.slug as string;
  const client = new GraphQLClient(
    `https://cloud.caisy.io/api/e/v4/${process.env.CAISY_PROJECT_ID}/graphql`,
    {
      headers: {
        "x-caisy-apikey": process.env.CAISY_API_KEY as string,
      },
    }
  );

  const gqlResponse: any = await client.request(
    gql`
      query allBlogArticle($slug: String) {
        allBlogArticle(where: { slug: { eq: $slug } }) {
          edges {
            node {
              text {
                json
              }
              title
              slug
              id
            }
          }
        }
      }
    `,
    { slug }
  );
  return gqlResponse?.allBlogArticle?.edges?.[0]?.node;
});

export default component$<{}>(() => {
  const post = useFetchData();
  return (
    <div>
      <h1>{post.value.title}</h1>
    </div>
  );
});

This code uses GraphQL to retrieve information about a specific blog article from an external API. It utilizes an API key and project ID for access and locates the article using a specific "slug."
It returns the title and text of the located article.

Rich text

To render the rich text, we use the library @caisy/rich-text-qwik-renderer, which can be installed with a package manager like this:

npm i @caisy/rich-text-qwik-renderer --save


After installing, we use the json from our text field to pass it as node to the RichTextRenderer It can be used in the page's template file like this:

src/pages/blog/[...slug]/index.tsx
import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
import RichTextRenderer from "@caisy/rich-text-qwik-renderer";
import { GraphQLClient, gql } from "graphql-request";

export const useFetchData = routeLoader$(async (requestEvent) => {
  const slug = requestEvent.params.slug as string;
  const client = new GraphQLClient(
    `https://cloud.caisy.io/api/e/v4/${process.env.CAISY_PROJECT_ID}/graphql`,
    {
      headers: {
        "x-caisy-apikey": process.env.CAISY_API_KEY as string,
      },
    }
  );

  const gqlResponse: any = await client.request(
    gql`
      query allBlogArticle($slug: String) {
        allBlogArticle(where: { slug: { eq: $slug } }) {
          edges {
            node {
              text {
                json
              }
              title
              slug
              id
            }
          }
        }
      }
    `,
    { slug }
  );
  return gqlResponse?.allBlogArticle?.edges?.[0]?.node;
});

export default component$(() => {
  const post = useFetchData();
  return (
    <>
      <h1>{post.value.title}</h1>
      <RichTextRenderer node={post.value.text.json} />
    </>
  );
});


If you navigate to the page, you should now see the headline and full rich text of the blog article displayed on the page.

caisy screenshot web tab my first article

With this, you have all you need to build a blog page with caisy and Qwik. For more complex queries and topics such as preview and localization, further reading may be necessary.