Typesafe, Fullstack React & GraphQL with AWS Amplify

This is the blog form of a talk I gave at React Summit and Reactathon 2020

Resources

I am going to skim over the slides, which are available here.

GitHub Repo for this demo: https://github.com/sw-yx/talk-typesafe-fullstack-react-demo-cms

I will also assume you already have the AWS Amplify CLI setup and configured.

Livecode Demo Script

First we clone our premade React + TypeScript app and initialize it as an AWS Amplify project:

git clone https://github.com/sw-yx/talk-typesafe-fullstack-react-demo-cms
cd talk-typesafe-fullstack-react-demo-cms
yarn
amplify init # select defaults for everything

At this point we have our simple app with some mock data:

https://user-images.githubusercontent.com/6764957/93261590-7c13f980-f7d5-11ea-9fda-b482ac1abdac.png

This gives us a strongly typed frontend with React and TypeScript! If you wish to learn how to wield React and TypeScript well, check out the React and TypeScript cheatsheet I have been maintaining for over two years!

Adding a GraphQL Database

We will now add a strongly typed backend to complement the frontend, using Amplify and AWS AppSync:

amplify add api

# choose the graphql option and defaults for the rest

? Please select from one of the below mentioned services: GraphQL
? Provide API name: myapiname
? Choose the default authorization type for the API API key
? Enter a description for the API key: 
? After how many days from now the API key should expire (1-365): 7
? Do you want to configure advanced settings for the GraphQL API No, I am do
ne.
? Do you have an annotated GraphQL schema? No
? Choose a schema template: Single object with fields (e.g., “Todo” with ID,
 name, description)

The following types do not have '@auth' enabled. Consider using @auth with @model
         - Todo
Learn more about @auth here: https://docs.amplify.aws/cli/graphql-transformer/directives#auth


GraphQL schema compiled successfully.

? Do you want to edit the schema now? Yes

We’ll use GraphQL SDL to define our database schema:

# amplify/backend/api/myapiname/schema.graphql
type Blog @model {
  id: ID!
  title: String!
  image: String!
  body: String!
}

That @model there is a special GraphQL directive, which AppSync uses to provision infrastructure for you alongside your GraphQL model via a library called GraphQL Transform. It has loads more goodies you can explore on your own, like @auth, @searchable, @function and @predictions, that you can add to your backend.

Provisioning this infrastructure in AWS takes a long time, so we’ll kick it off in the background while we work on the rest of the app:

amplify push -y # skips the yes check

Wiring up Backend to Frontend

We’re in the home stretch. We’ll need the aws-amplify library for interacting from the frontend:

yarn add -D aws-amplify

Notice that during amplify init an aws-exports.js file was generated in your src folder, with some non-secret information for your backend. We’ll use this to wire up our app with AWS Amplify:

// // src/index.tsx

// the other imports
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
Amplify.configure(awsconfig);

// rest of the app

You also notice that during amplify push there was an autogenerated folder in src/graphql with a bunch of GraphQL queries. We will use this in our app!

Optional step first - we can configure the codegen to generate typescript, so that types and auto imports work:

amplify codegen configure

? Choose the code generation language target typescript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/
graphql/**/*.ts
? Enter the file name for the generated code src/API.ts
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

Then we will use the listBlogs query in our app!

// src/App.tsx
import { API } from 'aws-amplify'; // new
import { listBlogs } from "./graphql/queries"; // new
// other imports here

function App() {
  
  // new
  React.useEffect(fetchBlogs);
  function fetchBlogs() {
    const query = API.graphql({ query: listBlogs }) as Promise<any>
    query.then(
      ({
        data: {
          listBlogs: { items },
        },
      }) => setBlogs(items)
    );
  }
  // etc
}

This sets up the blog items to refresh from backend whenever the app rerenders.

Then we’ll also do the same for adding and updating blogs:

// make sure to import createBlog and updateBlog

  async function addBlog(values: Blog) {
    const timestamp = new Date();
    const newBlog: Blog = {
      ...values,
      id: uuidv4(),
      createdAt: timestamp,
      updatedAt: timestamp,
    };
    setBlogs([...blogs, newBlog]);
    await API.graphql({query: createBlog, variables: {input: values}}) // NEW!
  }
  function _updateBlog(oldValues: Blog) {
    return async function (newValues: Blog) {
      const timestamp = new Date();
      const newBlog: Blog = {
        ...newValues,
        createdAt: oldValues.createdAt,
        updatedAt: timestamp,
      };
      setBlogs([...blogs.filter((x) => x.id !== oldValues.id), newBlog]);

      const { createdAt, updatedAt, ...input } = newBlog; // NEW!
      await API.graphql({ query: updateBlog, variables: { input } }); // NEW!
    };
  }

And there you have it! The basics of an end to end typed app!

You got stuck you can see the completed version of the app here https://github.com/sw-yx/talk-react-summit-demo-cms/blob/withAWS/src/App.tsx and the slides are here.

Alt Text

I’m sure you have questions - let’s hear them!

Tagged in: #react #graphql #aws

Leave a reaction if you liked this post! 🧡
Loading comments...
Webmentions
Loading...

Subscribe to the newsletter

Join >10,000 subscribers getting occasional updates on new posts and projects!

I also write an AI newsletter and a DevRel/DevTools newsletter.

Latest Posts

Search and see all content