DEV Community

Cover image for Medium like Blog Post Builder in React with graphql using typescript
Selva kumar
Selva kumar

Posted on

Medium like Blog Post Builder in React with graphql using typescript

This blog post is about will give you complete knowledge about how to start with react Graphql using typescript and the separation of concern and clean architecture folder structure.

Keep in mind that if you get stuck on any step, refer to the Github repo

To find the completed project, Demo link

This is what we are going to create:

Alt Text

What is GraphQL?

Before going forward, let me give a very brief overview of GraphQL and what all things we are going to discuss. So GraphQL is a query language for APIs that is developed by Facebook. It is an efficient alternative to REST because of its features such as:

  • With GraphQL there's no over fetching or under fetching of data, unlike REST.
  • Strongly typed graphQL schema which can be written in GraphQL Schema Definition Language (SDL) helps you validate your API requests during its compile time.
  • With the development of various GraphQL libraries (Apollo, Relay, etc) you are getting a lot of features such as caching, realtime data, etc.
  • It provides a large and amazing community! You can always get your queries answered whenever stuck.

This was just a basic introduction to GraphQL, but I recommend you to visit the site to gain deeper insights of the same.

What we'll do?

I will be mainly focussing on the front-end side, where I am gonna explain the two very useful react hooks useQuery and useMutation, how are we using these in our project for GraphQL operations along with the code.

Wait...what are GraphQL operations?

GraphQL provides various types of operations such as Query, Mutation, and Subscription which act as the entry points for the requests sent by the client. In this blog, I'll be discussing the first two types i.e. Query and Mutation.

I'm going to use graphql-hooks have two operations useQuery, useMutation

UseQuery: Basic syntax:
const { loading, error, data } = useQuery<Type1, Type2>(
    QUERY_STRING,
    { variables: <variable>,
onCompleted:()=>{console.log("query successful",data);}
,
onError:(error)=>{console.error(error);},
});

Enter fullscreen mode Exit fullscreen mode

data : The required data we are getting after the query is successful.
loading : It is a boolean value, if true, it means the query is still in flight. After it is successful the value of loading changes to false.
error : It stores the error if occurred while querying.

useMutation: Basic syntax:
 const [mutateFunction,{ error,loading}] = useMutation<Type>(MUTATION_STRING, {
    onCompleted: () => {
    console.log("details updated")
    },
    onError: (error) => {
    onError:(error)=>console.error(error);
    },
    refetchQueries: [{ query: QUERY_STRING, variables: <variable>],
  });

Enter fullscreen mode Exit fullscreen mode

mutateFunction : It is the mutate function which can be called anytime to run the mutation.
The second parameter is the object representing the mutation's execution status such as error , loading which have been explained above.

To use those operation in App need to wrap your app with the provider:

import { GraphQLClient, ClientContext } from 'graphql-hooks'

const client = new GraphQLClient({
  url: '/graphql'
})

function App() {
  return (
    <ClientContext.Provider value={client}>
      {/* children */}
    </ClientContext.Provider>
  )
}
Enter fullscreen mode Exit fullscreen mode

Now in your child components you can make use of useQuery


import { useQuery } from 'graphql-hooks'

const HOMEPAGE_QUERY = `query HomePage($limit: Int) {
  users(limit: $limit) {
    id
    name
  }
}`

function MyComponent() {
  const { loading, error, data } = useQuery(HOMEPAGE_QUERY, {
    variables: {
      limit: 10
    }
  })

  if (loading) return 'Loading...'
  if (error) return 'Something Bad Happened'

  return (
    <ul>
      {data.users.map(({ id, name }) => (
        <li key={id}>{name}</li>
      ))}
    </ul>
  )
}

Enter fullscreen mode Exit fullscreen mode

More details and options you can refer link

Our project's structure:

Before we begin let's talk about how our project is going to be organized.

Once we create our React app using create-react-app, we will have our base React app. Once inside that application, we will create a component pattern and Our folder structure will look like the following:

Alt Text

Now, let’s go over the folders one by one and the and understand the motivation behind them and the type of files you would store in them:

src/ - Contains all of our react codebase.
graphql - queries: This folder contains all static graphql query with props settings
pages - The name is pretty self-explanatory. It contains all the Stateful Components as discussed above.
views - This folder contains all the Presentational/Stateless Components as discussed above.
routes - This folder contains public, private, session differentiation and mainly for App navigation
start - Base react component.
types - Typescript related files or functions.
utils - Helper functions
use - custom hooks
templates - I create a different template for React with React Router 4 like global, error handling, promotions like so on.

Hopefully you understood how to setup a clean, efficient and maintainable folder structure for your React Apps.

Our goal today is to:

  1. Create a new React Blogger App
  2. Have Graphqlzero almansi Api
  3. Using React Routing, be able to navigate our app

Generate project with CreateReactApp:

I often (to not say always 😁) use Create React App to initiate my React projects.

In order to generate our project run :

npx create-react-app my-app --template typescript
Enter fullscreen mode Exit fullscreen mode

let's start coding.

Setting up React Router:

To get React Router going, we are going to need to install our dependency. In the project, run the following command:

npm install --save react-router-dom
Enter fullscreen mode Exit fullscreen mode

It define the route and call the Auth.

// src/start/Routes.tsx

routesTemplates.map((routesTemplate) => {
  const { routes: appRoutes, template: Template , type} = routesTemplate;
  return appRoutes.map( (appRoute) => {
    return (
      <Route
        exact={appRoute.exact}
        path={appRoute.path}
        key={appRoute.path}
        render={(route) =>
          <Auth
            appRoute={appRoute}
            Template={Template}
            route={route}
            type={type}
          />
        }
      />
    );
  });
})
Enter fullscreen mode Exit fullscreen mode

Route Types

There are three route types defined in the project.

// src/routes/routeTypes.tsx

export enum routeTypes {
  private = 'private',
  public = 'public',
  session= 'session',
}
Enter fullscreen mode Exit fullscreen mode

private: private pages like profile, edit-profile, etc. If the user isn’t logged then must to show the login page.
public: public pages like about-us, contact, etc.
session: session pages like login and sign-up. If the user is logged then must to redirect to the private dashboard.

Routes Template

In this file you can define the routes, the template and the rights (public, private, session).

// src/routes/index.tsx

const routesTemplate: IRouteTemplate[] = [
  {
    routes: privateRoutes,
    template: GlobalLayout,
    type: routeTypes.private,
  },
  ...
];
Enter fullscreen mode Exit fullscreen mode

Auth

Verify the rights and redirection.

// src/start/Auth.tsx

if (isPrivate(type) && !global.logged) {
  return <GlobalLayout Component={Error403} route={route} />;
}

if (isSession(type) && global.logged) {
  return <Redirect to="/" />
}

const Layout = appRoute.template ? appRoute.template : Template;
return <Layout
  Component={appRoute.component}
  route={route}
/>;
Enter fullscreen mode Exit fullscreen mode

Building our React components

In this application, we are going to have fives pages for templates:

  • All Posts List with Author Details
  • All Author List with Posts listing
  • Post details

Alt Text

Putting it all together

Now that we have our components set up, we can head on over to “localhost:3000” and see all pages get render.

Feel free to ask questions in any area you don't understand.

To be continued on next article about Graphql implementation...

Oldest comments (2)

Collapse
 
avilde profile image
Andris Vilde

Decent post!
I would suggest formatting your code blocks, spotted some inconsistencies ) ✍

Collapse
 
earnestat profile image
earnestat

Great post. Thank you Selva.
Couldn't access the github repo. Could you share the correct link?