Typescript, NextJS, Strapi, and GraphQL: An Overview Of How I Created My Blog
Tags: react, webdev, softwaredevelopment, javascript
Published: 2023-02-03 05:21:52

---
title: Typescript, NextJS, Strapi, and GraphQL: An Overview Of How I Created My Blog
published: true
date: 2023-02-03 04:48:18 UTC
tags: react,webdevelopment,softwaredevelopment,javascript
canonical_url: 
---

First things first, let's introduce the stack we are going to use. Typescript is "Is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale." in simple words, Typescript enhances coding safety and enables more OOP for the web.

NextJS is a framework that "Enables you to create full-stack web applications by extending the latest React features" in simple words, NextJS is an upgrade over the widely used ReactJS library.

Strapi is an open-source headless CMS. CMS stands for "content management system," allowing us to create, manage, modify, and publish content in a user-friendly interface without programming knowledge. Headless means there is no built-in end-user UI; we can use whatever frontend technology we want.

GraphQL is "A query language for APIs and a runtime for fulfilling those queries with your existing data" in simple words, it's a language used to query data and get the fields you want.

Note: this is an overview, which means there are aspects I will not go over, such as styling and best HTML tags; this blog is meant to showcase the functionality of creating a Blog using the technologies mentioned above.

### Initializing The Project

Now, let's create the folders using the command line (because it's cooler), so the first command is to create a folder containing all our files.

```
mkdir my-personal-website
```

Now, let's create folders for both NextJS and Strapi.

```
cd my-personal-website
mkdir frontend, strapi
```

#### Initialize Strapi

```
cd strapi
npx create-strapi-app@latest . --ts --quickstart
```

Parameter `@latest` means it will install the latest version, `.` means we want to create the project in the current folder, then `— ts` means it will use TypeScript, and lastly `— quickstart` it will install with default settings; when the installation finishes, it will open a new window on the address `/admin/auth/register-admin`, asking you to create a new admin account; go ahead and create a new account. Note: using `— ts` is optional since I won't change the source code in this post.

#### Initialize NextJS

```
cd frontend
npx create-next-app@latest . --ts
```

And lastly, remember to init a Github repo, but I'll not cover it in this post.

### Starting with Strapi

I like to start with the backend in my projects, so I will start with Strapi as it's considered a backend.

#### Installing Graphql

One advantage of using Strapi is its easy integration with Graphql. To install the Graphql plugin, we need the run the following command.

```
npm run strapi install graphql
```

If you encounter the `_Duplicate "graphql" modules` error, don't worry; it's easy to fix; all you need to do is to delete the **_node\_modules_** folder and **_package-lock.json_** , then we need to reinstall packages.

```
rm node_modules
rm package-lock.json
npm install
npm run strapi dev
```

After successful installation, we can now visit the URL `/graphql`.

#### Creating The Blog Post Collection

Open the Strapi homepage on URL `http://localhost:1337/admin/`, and after you sign in, go to the **_Content-Type Builder_** page, click on **_Create new collection type_** button, then in the **_Display name_** field, fill it in with an appropriate name; I will choose **_Blog Post_** , then click continue.

Now, we are in fields selection for our new collection; you can customize it however you want; for the sake of simplicity, I will add three fields, a **_Text_** field for the title, another **_Text_** field for the description, and lastly, a **_Rich Text_** field for the body, next click save.

Now that we have a collection, we need to populate it with data, go to **_Content Manager_** , and now we can see the newly created collection, click on it, then **_Create new entry_** button, and fill it with any data you want, then click save and publish.

#### Accessibility Option 1: Publicly Accessible

Before we can access our data through Graphql, we need to make it accessible; you can make it publicly accessible or only for authenticated users. I will choose a publicly accessible collection since I'm not storing sensitive data.

To access it publicly, go to **_Settings,_** then roles, click on **_Public_** , scroll down to the collection you created (in my case, it's Blog-post), click it, then tick **_find_** and  **_findOne._**

#### Accessibility Option 2: Authenticated Only

However, should you choose the authenticated way, here is how to do it. There are two ways to achieve this; the first one is to create **_API Token_** in the settings page, then we need to attach it with our requests by adding it to the headers as an **_Authorization_** header and setting the value to be **_bearer <API Token>_** , the second way is to create a new entry in the **_User_** collection, via **_Content Manager_** page, then send a **_Post_** request to `/api/auth/local` with an attached JSON body containing identifier which could be either the username or email, and password, of the newly created user in the **_User_** collection, **NOT THE ADMIN ACCOUNT WE CREATED IN INITIALIZE STRAPI.**

```
{
 "identifier": "email or username",
 "password": "password"
}
```

If the request were successful, you would get a response containing the **_JWT_** field, and now we need to do the same thing, send it with each request via headers in an **_Authorization_** header and set the value to be **_bearer <API Token>._**

Now to access it, go to **_Settings,_** then roles, click on **_Authenticated_** , scroll down to the collection you created (in my case, it's Blog-post), click it, then tick **_find_** and  **_findOne._**

#### Testing Graphql Endpoint

To access Graphql go to `/graphql`, and now, we can see the input field on the left side; here, we will put our queries to query our Strapi application, an example of a query:

```
query {
  blogPosts {
    data {
      id
      attributes {
        title
        description
        body
      }
    }
  }
}
```

And in my case, the response is:

```
  "data": {
    "blogPosts": {
      "data": [
        {
          "id": "1",
          "attributes": {
            "title": "test",
            "description": "test",
            "body": "test"
          }
        }
      ]
    }
  }
}
```

#### Testing Rest Endpoint

All we need to do is send a GET request to `/api/blog-posts/`. An example of a response would be:

```
{
    "data": [
        {
            "id": 1,
            "attributes": {
                "title": "test",
                "description": "test",
                "body": "test",
                "createdAt": "2023-01-30T20:28:28.141Z",
                "updatedAt": "2023-01-30T20:28:29.027Z",
                "publishedAt": "2023-01-30T20:28:29.025Z"
            }
        }
    ],
    "meta": {
        "pagination": {
            "page": 1,
            "pageSize": 25,
            "pageCount": 1,
            "total": 1
        }
    }
}
```

We have finished the Strapi section and will continue with NextJS.

### Starting With NextJS

Create a new directory inside the **_pages_** directory and name it **_blog_** ; next, create a new `index.tsx` file and initialize a basic react component.

```
import {NextPage} from "next";

interface Props {
}

const About: NextPage<Props> = (Props) => {
    return (
        <p>About Page</p>
    )
}

export default About;
```

Now we are to visit `blog` and view the About Page.

#### Creating Interfaces

We will need a few interfaces; let's start by creating a **_common_** folder containing all of our basic modules, then create another folder called **_types_** which will contain all our types and interfaces then create a new typescript I will name **_BlogInterfaces_** ; now let the first interface, which is going to `IBlogIdentification` and it will contain our identification field; in our case, it's the id of the blog post.

```
export interface IBlogIdentification {
    id: string
}
```

Next, we need an interface to hold the blog fields' title, description, and body; name it `IBlogFields`.

```
export interface IBlogFields {
    title: string,
    description: string,
    body: string
}
```

Next, we need an interface for blog attributes we will receive from the GraphQL query; name it `IBlogAttributes`.

```
export interface IBlogAttributes {
    attributes: Partial<IBlogFields> & Pick<IBlogFields, 'title'>
}
```

I have used Partial and Pick utility types to make all of `IBlogFields` fields optional except title.

Lastly, we will need an interface to handle the whole blog entry; name it `IBlog`.

```
export interface IBlog extends IBlogIdentification, IBlogAttributes{}
```

Now let's update the Props interface on the **_blog_**  page.

```
interface Props {
    blogs: IBlog[]
}
```

#### GraphQL Works

Before we start fetching data, we need to do GraphQL Works. First, we need to install two packages to be able to interact with GraphQL; we will need the `graphql` and `@apollo/client packages`.

```
npm i graphql, @apollo/client
```

Second, we need to write the queries; we will need three queries, the first to list blogs, the second is to get a single blog, and the third is to get the list of blog Ids'; in the **_common_** folder, create a new folder and call it graphql, then create a new file and call it `queries.tsx`, and add the following queries.

```
export const LIST_BLOG = gql(`query {
  blogPosts {
    data {
      id
      attributes {
        title
      }
    }
  }
}`)

export const SINGLE_BLOG = gql(`query ($blogId: string!) {
  blogPosts(filters: {id: {eq: $blogId}}) {
    data {
      id
      attributes {
        title
        description
        body
      }
    }
  }
}`)

export const LIST_ID = gql(`query {
  blogPosts {
    data {
      id
    }
  }
}`)
```

The third thing we need is to store Strapi's address, and the most convenient way is to store it inside the **_env_** variables; NextJS already comes with a built-in environment variable module, so there is no need to install `dotenv`, all we need to do is to go to `next-config.js`, and add the following export.

```
module.exports = {
  env: {
    STRAPI_ADDRESS: "http://127.0.0.1:1337/graphql"
  },
  nextConfig,
}
```

And the last thing we need is an `ApolloClient` to be to connect with the GraphQL server, inside the **_graphql_** folder, create a new file and name it `client.tsx`, then add the following code.

```
import {ApolloClient, InMemoryCache, NormalizedCacheObject} from "@apollo/client";

const client: ApolloClient<NormalizedCacheObject> = new ApolloClient({
    uri: process.env.STRAPI_ADDRESS,
    cache: new InMemoryCache(),
});

export default client;
```

Note: the cache is a way for the `ApolloClient` to cache results, it's a big topic and out of scope for this post. More info can be found [here](https://www.apollographql.com/docs/react/caching/overview).

#### Fetching Data

Now we can start implementing the fetching procedure; let's start by creating the `getStaticProps` function:

```
export const getStaticProps: GetStaticProps<Props> = async () => {
    
}
```

We are going to use `getStaticProps` because it will pre-render the page at build time, and the data will not be changing consistently; there is no need to send a request to our CMS every time.

Now we can start querying Strapi; let's get a list of blogs.

```
    const {data} = await client.query({query: LIST_BLOG})
    const blogs: IBlog[] = data.blogPosts.data;
    
    return {
        props: {
            blogs
        }
    }
```

#### Displaying Blog List

Now that we can retrieve a list of blogs, we can start displaying them; we need new folders. To make it easier, I will show a demo below of my project structure.

```
common
--elements
----blog
------BlogTitle.tsx

modules
--blog
----BlogCardList.tsx
```

We need two files, `BlogTitle` will render the title of a blog, and `BlogCardList` will use `BlogTitle` to render a list of blogs, let's start with `BlogTitle`.

```
interface Props {
    title: string
    isClickable: boolean
    blogId?: string
}

const BlogTitle: NextPage<Props> = ({title, isClickable, blogId}) => {
    if(isClickable && blogId) {
        return (
            <Link href={`/blog/${blogId}`}>{title}</Link>   
        )
    } else {
        return (
            <p>{title}</p>
        )
    }
}
```

Note: Because I know in the commercial version, I will be using the blog title in multiple locations, and it will have more props and functionality attached to it, I chose to make the blog title a separate component; however, you can choose not to.

Now to the list of cards.

```
interface Props {
    blogs: IBlog[]
}

const BlogCardList: NextPage<Props> = ({blogs}) => {
    return (
        <div>
            {blogs.map((blog, i) => {
                return (
                    <div key={i}>
                        <BlogTitle 
                            title={blog.attributes.title} 
                            isClickable={true} 
                            blogId={blog.id}
                        />
                    </div>
                );
            })}
        </div>
    )
}

export default BlogCardList;
```

Now to show the list, we need to add `BlogCardList` to about page.

```
const Blog: NextPage<Props> = ({blogs}) => {
    return (
        <BlogCardList blogs={blogs}/>
    )
}
```

Now if we go to `/blog`, we should see a list of blog titles.

#### Displaying A Single Blog Post

Let's start creating the individual blog post page, create a new file, and call it `[id].tsx` inside the **_blog_** directory inside the **_pages_** directory, initialize a new basic react component.

```
import {NextPage} from "next";
import {IBlog} from "@/common/types/BlogInterfaces";

interface Props {
    blog: IBlog
}

const client = new ApolloClient({
    uri: process.env.STRAPI_ADDRESS,
    cache: new InMemoryCache()
});

const Blog: NextPage<Props> = ({blog}) => {
    return (
        <p>Blog Here<p/>
    )
}

export default Blog;
```

We are going to utilize NextJS's `getStaticPaths` and `getStaticProps` functions, more info can be found [here](https://nextjs.org/docs/api-reference/data-fetching/get-static-paths).

```
export const getStaticPaths: GetStaticPaths = async () => {
    const {data} = await client.query({query: LIST_ID })
    const ids: IBlogIdentification[] = data.blogPosts.data;
    
    const paths = ids.map(id => {
        return {params: {...id}}
    })
    
    return {
        paths,
        fallback: true
    }
}

export const getStaticProps: GetStaticProps<Props> = async ({params}) => {
    console.log()
    const {data} = await client.query({query: SINGLE_BLOG, variables: {blogId: params!.id}})
    const blog: IBlog = data.blogPosts.data[0];

    return {
        props: {
            blog
        }
    }
}
```

And now, we can access the blog data inside the component.

```
const Blog: NextPage<Props> = ({blog}) => {
    console.log(blog)
    return (
        <div>
            <p>{blog.id}</p>
            <p>{blog.attributes.title}</p>
            <p>{blog.attributes.description}</p>
            <p>{blog.attributes.body}</p>
        </div>
    )
}
```

Now our blog is functional; we can retrieve and display a list of blogs and a single blog post.