Part 1 : Redesigning my Website with the t3-stack
Tags: typescript, nextjs, trpc, t3stack
Published: 2022-07-27 08:04:00

## Quick Introduction

This is a series of blog posts which detail how I'm setting up my website with the t3 stack. If you haven't read the previous articles, I suggest looking at 

- Part 1. : [Redesigning My Website with the t3-stack](https://www.ivanleo.com/blog/setting-up-the-new-blog)
- Part 2 : [Displaying Individual Articles](https://www.ivanleo.com/blog/displaying-individual-articles)
- Part 3 : [Adding a table of contents to our blog articles](https://www.ivanleo.com/blog/adding-a-table-of-contents-to-blog-articles)
- Part 4 : [Tidying Up the final product](https://www.ivanleo.com/blog/tidying-up-the-article-page-and-adding-ssg-to-the-home-page)

You can see the website [here](ivanleo.com)




## Initial Set-up

Recently I chanced upon the t3 stack when browsing upon twitter so I decided to work on implementing it by creating a new t3 app using the `create-t3-app` npm package. However, before I could do much, I found this specific error when building the app on Vercel.

![Screenshot 2022-07-27 at 2 32 18 PM](https://user-images.githubusercontent.com/45760326/181177510-2f4e5d51-04f8-43fb-99f5-f573944ce8e6.png)

After doing a bit of sleuthing within the repo that `create-t3-app` had created, I traced it down to a specific file, that is `./src/env/schema.mjs` which had a line of code as 

```mjs
// @ts-check
import { z } from "zod";

/**
 * Specify your server-side environment variables schema here.
 * This way you can ensure the app isn't built with invalid env vars.
 */
export const serverSchema = z.object({
  DATABASE_URL: z.string().url(),
  NODE_ENV: z.enum(["development", "test", "production"]),
  NEXTAUTH_SECRET: z.string(),
   NEXTAUTH_URL: z.string().url(),
  DISCORD_CLIENT_ID: z.string(),
  DISCORD_CLIENT_SECRET: z.string(),
});
...

```

This was then being called in `client.mjs` and `server.mjs` during the build process which led to the error being thrown. The fix is outlined below.

```mjs
export const serverSchema = z.object({
  // DATABASE_URL: z.string().url(),
  NODE_ENV: z.enum(["development", "test", "production"]),
  // NEXTAUTH_SECRET: z.string(),
  // NEXTAUTH_URL: z.string().url(),
  // DISCORD_CLIENT_ID: z.string(),
  // DISCORD_CLIENT_SECRET: z.string(),
});

```


Let's get started!

## Github Issues as a CMS

For this specific portion, we'll be using Github's official npm package `octokit`  so run the following command 

```
npm install @octokit/graphql
```

before starting the rest of this section 

### Setting up a personal access token

> **Note** : A personal access token is used by Github to allow you to access information about your repositories and code. You'll need it for this particular project in order to use Github Issues as a CMS

What you'll want to do is to first go to github.com and then navigate to your settings and click on **Developer Settings**

<img width="217" alt="Screenshot 2022-07-27 at 2 25 58 PM" src="https://user-images.githubusercontent.com/45760326/181176531-10cf6e85-b705-4e0f-9ff8-52f477b4b78b.png">

You'll then want to click on **Personal Access Tokens**

<img width="798" alt="Screenshot 2022-07-27 at 2 27 21 PM" src="https://user-images.githubusercontent.com/45760326/181176744-3628ab4a-eccd-468d-88e6-4de8eb192754.png">

Lastly, you'll need to then configure your access token so it only has access to **public_repo** as seen blow

<img width="554" alt="Screenshot 2022-07-27 at 2 30 19 PM" src="https://user-images.githubusercontent.com/45760326/181177152-4feb1625-2b4a-4e39-8975-eb47fa0371c5.png">

You should then update this token in your local .env file

![Screenshot 2022-07-27 at 2 31 03 PM](https://user-images.githubusercontent.com/45760326/181177274-f4f4f87d-9325-4c4d-8ec1-9d9716052386.png)

> Don't forget to restart your development server so that the new .env file is loaded in.

### Configuring the TRPC router

If you've used the `create-t3-app` in order to configure your project, everything should be set-up nicely for you. Let's try to make a github-specific in this case. Go to `src/server/router/index.ts` and you'll find the following code

```
// src/server/router/index.ts
import { createRouter } from "./context";
import superjson from "superjson";

import { exampleRouter } from "./example";
import { protectedExampleRouter } from "./protected-example-router";

export const appRouter = createRouter()
  .transformer(superjson)
  .merge("example.", exampleRouter)
  .merge("question.", protectedExampleRouter);

// export type definition of API
export type AppRouter = typeof appRouter;
```

> If you've ever used express before, you'll realise that TRPC works remarkably similar. We can ensure a modular separation of individual routes by simply working with the `.merge` function. Let's create a new router to handle our github issues as seen below

```js
import { createRouter } from "./context";
import { z } from "zod";

export const githubRouter = createRouter().query("hello", {
  input: z
    .object({
      text: z.string(),
    })
    .nullish(),
  resolve({ input }) {
    const { text } = input;
    return {
      greeting: `Hello from github router. You indicated ${text}`,
    };
  },
});
```

We can now attach this to our main `appRouter` in our `index.ts` file as seen below

```js
// src/server/router/index.ts
import { createRouter } from "./context";
import superjson from "superjson";

import { githubRouter } from "./github-router";

export const appRouter = createRouter()
  .transformer(superjson)
  .merge("github.", githubRouter);

// export type definition of API
export type AppRouter = typeof appRouter;
``` 

and call it on the frontend by modifying `index.tsx` as 

```
import type { NextPage } from "next";
import Head from "next/head";
import { trpc } from "../utils/trpc";

type TechnologyCardProps = {
  name: string;
  description: string;
  documentation: string;
};

const Home: NextPage = () => {
  const hello = trpc.useQuery(["github.hello", { text: "from tRPC" }]);
  console.log(hello?.data?.greeting);

  return <>Welcome to Tailwind</>;
};

export default Home;
```

This gives us the following output in our console, indicating that we have successfully linked the frontend and our backend.

<img width="469" alt="Screenshot 2022-07-27 at 2 51 17 PM" src="https://user-images.githubusercontent.com/45760326/181180782-e0fd3bc3-c16c-4448-8a18-544e2af100ad.png">

> The reason why we have four different console logs is because `create-t3-app` automatically enables SSR for us so the content is automatically rendered on the server before being sent to the frontend, thus your frontend code runs twice.

### Getting a list of posts 

Let's first get our list of issues from github. You can do so by using the following code snippet below

```js
import { graphql } from "@octokit/graphql";

const graphqlWithAuth = graphql.defaults({
  headers: {
    authorization: `token ${process.env.GITHUB_TOKEN}`,
  },
});

export const getPosts = async () => {
  const { repository } = await graphqlWithAuth(`
        {
          repository(owner: <your github username>, name: <the repository you're importing the issues from>) {
            issues(last: 50) {
              edges {
                node {
                    title
                    createdAt
                    labels(first: 3) {
                        nodes{
                            name
                        }
                    }
                }
              }
            }
          }
        }
      `);

  console.log(repository);

  return repository;
};
```

> **Note** : You can find more information on the API at the [github documentation](https://docs.github.com/en/graphql/reference/objects#label).


 In my specific issue list, I have two tags `draft` and `published`. I only want to get a list of the issues that are tagged with the `published` tag. We can do so by performing a filter over the returned object that we obtain

```
export const getPublishedPosts: () => Promise<githubPostTitle[]> = async () => {
  const { repository } = await graphqlWithAuth(`
        {
          repository(owner: <your github username> , name: <your repository where the issues are in>) {
            
            issues(last: 50) {
              edges {
                node {
                    title
                    createdAt
                    labels(first: 3) {
                        nodes{
                            name
                        }
                    }
                }
              }
            }
          }
        }
      `);

  return repository.issues.edges
    .map(({ node }) => {
      return { ...node };
    })
    .filter((post: githubPostTitle) => {
      return post.labels.nodes[0].name === "published";
    });
};
```

We also add in a type we define called `githubPostTitle` in the code as seen below

```
type githubPostStatus = "draft" | "published";

type githubPostTitle = {
  createdAt: string;
  title: string;
  labels: {
    nodes: [{ name: githubPostStatus }];
  };
};
```

Now all we have to do is to call this in our `github-router.ts` file which stores our github router and we have a route we can call from our frontend that displays our list of posts as seen below.

> **Note: ** A router in this case is simply a collection of queries and mutations that we define. Each query and mutation has an associated resolve method in order to get a specific output. We can ensure we have typesafe APIs by using `zod` which has a number of wrapper functions in order for us to use.

```js
import { createRouter } from "./context";
import { z } from "zod";
import { getPublishedPosts, githubPostTitle } from "../../utils/github";

export const githubRouter = createRouter().query("get-posts", {
  async resolve({ input }) {
    const repositoryInformation: githubPostTitle[] = await getPublishedPosts();
    return {
      posts: repositoryInformation,
    };
  },
});

```

### Rendering our posts on the frontend

We can now call our specific route by using the custom react hook `useQuery`

```
const posts = trpc.useQuery(["github.get-posts"]);
```

This in turn returns objects that look like this

```
posts : [
    {
        "title": "Setting up a development enviroment",
        "createdAt": "2022-07-27T06:14:56Z",
        "labels": {
            "nodes": [
                {
                    "name": "published"
                }
            ]
        }
    },
    ...
]
```

Notice that once we added the filter, all our issues tagged as `draft` disappeared. We can then map over this list of posts to render if on the screen

```
import type { NextPage } from "next";
import Head from "next/head";
import { trpc } from "../utils/trpc";

const Home: NextPage = () => {
  const posts = trpc.useQuery(["github.get-posts"]);

  return (
    <>
      <h1>Posts</h1>
      {posts.data?.posts?.map((item) => {
        return <p key={item.title}>{item.title}</p>;
      })}
    </>
  );
};

export default Home;
```

which in turn produces 

<img width="796" alt="Screenshot 2022-07-27 at 4 01 24 PM" src="https://user-images.githubusercontent.com/45760326/181194422-726a7bb2-7101-469b-ad94-8547630461fc.png">

Voila! Now we've hooked up our github issues to our NextJS frontend using TRPC. 

In the next part of this series, we'll move on to writing out a custom page that will be able to display the markdown in each specific page properly. 

> I've published the next part of this series, you can read it [here](https://dev.to/ivanleomk/part-2-rendering-github-issues-from-markdown-to-html-on-my-blog-5hme)