Giter Club home page Giter Club logo

e2e-ts-gql-workshop's Introduction

๐Ÿ‘จโ€๐Ÿ’ป End-to-end Type-Safe GraphQL Workshop

Get introduced to the marvellous GraphQL universe! Discover all t`he benefits for adding End-to-end Type-Safety to your GraphQL App taking all the advantages of TypeScriot, GraphQL & React working together. Apollo, auto-gen Types & Hooks would build the pillars of a SpaceX demo ๐Ÿ›ฐ

๐ŸŽฏ Goals

  • Understand Why, How & What's GraphQL
  • Create a J/TS GraphQL Server
  • Create a J/TS GraphQL Client
  • Contribute with Open Source

๐Ÿ—“ Agenda

๐Ÿš€ Server

  • S0: JS GraphQL Server Demo
  • S1: JS GraphQL Server
  • S2: TS GraphQL Server

๐ŸŒ– Client

  • S0: JS REST Client
  • S1: JS GraphQL Client
  • S2: TS GraphQL Client

๐Ÿ›  Requirements

๐Ÿ‘ Essentials

  • Node. Latest LTS Version (10.15.3).
  • Git. Latest LTS Version (2.21.0).
  • Github. You just need to create an account!

๐Ÿค™ Nice to have

  • VSCode. The futuristic J/TS IDE!
  • CodeCopy. Copy to clipboard on markdown.

๐ŸŒ Intro

Open a terminal

Fork and clone this repository

git clone https://github.com/${๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป}/e2e-ts-gql-workshop

Navigate to the created folder

cd e2e-ts-gql-workshop

๐Ÿš€ Server

Time -0, it'll be time to liftoff creating (from scratch) a GraphQL Server. TypeDefs, resolvers & context will be the boosters of the GraphQL JS implementation. Then, we'll take all the advantages of having a strongly typed GraphQL Schema to auto-generate the TypeScript types based on our GraphQL type definitions!

Summary

  • S0: JS GraphQL Server Demo
  • S1: JS GraphQL Server
  • S2: TS GraphQL Server

Step 0๏ธโƒฃ JS GraphQL Server Demo

In this step will create a demo JS GraphQL Server based on mock data! Summary

  • Create folder structure
  • Implement JS GraphQL Server
  • Run Server

๐Ÿ“ฆ Create folder structure

๐Ÿ‘‰ Checkout first step

git checkout server-step-0

๐Ÿ‘ Create server folder

mkdir server

๐Ÿ‘Œ Create index.js inside the server folder

cd server
touch index.js

๐ŸŽฎ Implement JS GraphQL Server

Let's implement a demo GraphQL server in our index.js

๐Ÿ‘‰ Declare typeDefs

const typeDefs = gql`
  # Entry Points
  type Query {
    launches: [Launch]
    launch(id: Int!): Launch
  }

  # Types
  type Launch {
    flight_number: Int!
    mission_name: String!
    details: String
    links: LaunchLinks
    rocket: LaunchRocket
  }
  type LaunchLinks {
    flickr_images: [String]
  }
  type LaunchRocket {
    rocket_name: String!
  }
`;

๐Ÿ‘‰ Declare resolvers

const resolvers = {
  Query: {
    launches: (obj, args, context) => launches,
    launch: (obj, args, context) =>
      launches.find(launch => args.id === launch.flight_number)
  }
};

๐Ÿ‘‰ Declare server

const server = new ApolloServer({
  typeDefs,
  resolvers
});

๐Ÿ‘‰ Call server

server.listen().then(({ url }) => console.log(`๐Ÿš€ Server ready at: ${url}`));

๐Ÿ‘ Import mock data

const launches = [...]

๐Ÿ‘Œ Add dependencies

const gql = require("graphql-tag");
const { ApolloServer } = require("apollo-server");

โšก๏ธ Run Server

๐Ÿ‘‰ Install dependencies

npm install graphql apollo-server

๐Ÿ‘ Run server

node ./index.js

๐Ÿ‘Œ Explore GraphQL API http://localhost:4000/

Well done ๐Ÿ’ช, commit your changes and let's get our hands dirty!

Step 1๏ธโƒฃ JS GraphQL Server

In this step we will connect the server with a database and we will implement an underfecthing solution

Summary

  • Explore the DB
  • Create DB connector
  • Adapt server & resolvers
  • Underfetching implementation

๐Ÿง Explore the DB

Let's discover how to access to SpaceX real data ๐Ÿ”Ž

๐Ÿ‘‰ Open Humongous

๐Ÿ‘‰ Login on Get Started and choose your fav MongoDB hosting provider

๐Ÿ‘‰ Paste the SpaceX Public DB link

mongodb+srv://public:[email protected]/spacex-api

๐Ÿ‘ Click Next & Create Project

๐Ÿ‘Œ Explore all the data collections

๐Ÿ•น Create DB connector

Now, we are gonna get rid of the mocks to use real data ๐Ÿ”ฅ

๐Ÿ‘‰ Create db.js file

touch db.js

๐Ÿ‘‰ Connect to DB

const url =
  "mongodb+srv://public:[email protected]/spacex-api";

const getDB = async () => {
  const client = await MongoClient.connect(url, {
    poolSize: 20,
    useNewUrlParser: true
  });

  return client.db("spacex-api");
};

module.exports = {
  getDB
};

๐Ÿ‘ Add mongodb dependendy

const MongoClient = require("mongodb");

๐Ÿ‘Œ Install mongodb dependency

npm install mongodb

๐Ÿ’ซ Adapt server & resolvers

๐Ÿ‘‰ Adapt our server on index.js replacing the current definition with:

(async () => {
  const db = await getDB();

  const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: { db }
  });

  server.listen().then(({ url }) => console.log(`๐Ÿš€ Server ready at ${url}`));
})();

๐Ÿ‘‰ Import the DB

const { getDB } = require("./db");

๐Ÿ‘‰ Implement resolvers replacing the current with:

const resolvers = {
  Query: {
    launches: async (obj, args, context) => {
      const data = await context.db
        .collection("launch")
        .find()
        .limit(10)
        .toArray();
      return data;
    },
    launch: async (obj, { flight_number }, context) => {
      const [data] = await context.db
        .collection("launch")
        .find({ flight_number })
        .limit(1)
        .toArray();
      return data;
    }
  }
};

๐Ÿ‘‰ Remove launches on index.js

๐Ÿ‘ Run server

node ./index.js

๐Ÿ‘Œ Explore explore real data through the GraphQL API (refresh your browser) http://localhost:4000/

โ™ป๏ธ Underfetching implementation

๐Ÿ‘‰ Add rocket to LaunchRocket

type LaunchRocket {
  rocket_name: String!
  rocket: Rocket
}

๐Ÿ‘‰ Include the Rocket type

type Rocket {
  id: ID!
  name: String
  description: String
  cost_per_launch: Int
}

๐Ÿ‘ Add the LaunchRocket resolver

const resolvers = {
  Query: {...},
  LaunchRocket: {
    rocket: async (obj, args, context) => {
      console.log("obj: ", obj);
      const [data] = await context.db
        .collection("rocket")
        .find({ id: obj.rocket_id })
        .limit(1)
        .toArray();
      return data;
    }
  }
}

๐Ÿ‘Œ Run the server & explore real data (refresh your browser)

node ./index.js

Look ๐Ÿ‘€ what you're getting on obj when asking for launches { rocket { rocket { ... } } } (don't forget to remove the console.log)

2/3 โœ…, creating APIs has never been easier, commit your changes and let's move on to the last step!

Step 2๏ธโƒฃ TS GraphQL Server

In this step will auto-generate TypeScript types based on our GraphQL implementation to make our API type safe

Sumary

  • Explore API & Codebase
  • Evolve the API
  • Generate TS types
  • Evolve Safely the API

๐Ÿค” Explore API & Codebase

๐Ÿ‘‰ Checkout

git checkout server-step-2

๐Ÿ‘‰ Install dependencies & run the server

cd server
npm install
npm start

๐Ÿ‘‰ Explore the API http://localhost:4000

๐Ÿ‘ Explore the codebase

๐Ÿ‘Œ Try to understand how this query is being resolved

{
  history(id: 17) {
    title
    flight {
      mission_name
      rocket {
        rocket {
          name
          cost_per_launch
        }
      }
    }
  }
}

๐ŸŽธ Evolve the API

๐Ÿ‘ Add to Rocket typeDefs rocketByName

extend type Query {
  ...
  rocketByName(name: String!): Rocket
}

๐Ÿ‘Œ Add it resolver

rocketByName: async (obj, { name }, context) => {
  const [data] = await context.db
    .collection(collection)
    .find({ name })
    .limit(1)
    .toArray();
  return data;
};

๐Ÿ‘ Run server

node ./index.js

๐Ÿ‘Œ Explore GraphQL API testing the new evolution (refresh your browser) http://localhost:4000/

If everything went well, commit your changes!

๐ŸŽถ Generate TS types

๐Ÿ‘‰ Checkout

git checkout server-step-3

๐Ÿ‘‰ Install dependencies & run the server

npm install
npm run dev

๐Ÿ‘‰ Create codegen.yml file

touch codegen.yml

๐Ÿ‘‰ Include the configuration

schema: src/schema/**/*.ts
overwrite: true
watch: true
require:
  - ts-node/register
generates:
  ./src/types/types.d.ts:
    config:
      contextType: ./context#MyContext
    plugins:
      - typescript-common
      - typescript-server
      - typescript-resolvers

Indentation here is crucial!

๐Ÿ‘‰ Install codegen dependencies

๐Ÿ‘‰ Install dependencies

npm install

๐Ÿ‘ Open a terminal and run

npm run dev

๐Ÿ‘Œ Explore the API http://localhost:4000

๐Ÿ‘ Open another terminal and run

npm run generate

๐Ÿ‘Œ Explore types/types.d.ts file

Commit your changes and let's wrap this up!

๐ŸŽป Evolve Safely the API

๐Ÿ‘‰ Add again rocketByName into the Rocket typeDefs

  extend type Query {
    ...
    rocketByName(name: String!): Rocket
  }

๐Ÿ‘‰ Explore the types/types.d.ts file changes

๐Ÿ‘‰ Add again it resolver

rocketByNome: async (obj, { nome }, context) => {
  const [data] = await context.db
    .collection(collection)
    .find({ nome })
    .limit(1)
    .toArray();
  return data;
};

๐Ÿ‘‰ Type your rocket's resolvers

const Query: QueryResolvers.Resolvers = {
  ...
}

๐Ÿ‘ Import the QueryResolvers type definitions

import { QueryResolvers } from "../../types/types";

๐Ÿ‘Œ Fix all the erros that you could find with the help of TypeScript!

Step ๐Ÿ‘ฝ Create a REST API based on GraphQL

๐Ÿ‘‰ Checkout

git checkout server-step-5

๐Ÿ‘‰ Install dependencies & run the server

npm install
npm run dev

๐Ÿ‘ Explore the GraphQL API http://localhost:4000/graphql ๐Ÿค™ Explore the REST API http://localhost:4000/rest

๐Ÿ‘Œ Explore the codebase Take a look at the servers folder, excluding that folder eveything is same than last step!

GraphQL ๐Ÿ’œ REST

๐ŸŽ‰ You're just finished all GraphQL Server steps, let's now learn about GraphQL Clients!


๐ŸŒ– Client

Approaching landing... we will create a React-Apollo client in JS (using hooks, of course). Later on we will evolve it safely generating the TypeScript types from our GraphQL documents!

Summary

  • S0: JS REST Client
  • S1: JS GraphQL Client
  • S2: TS GraphQL Client

Step 0๏ธโƒฃ JS REST Client

Summary

  • Create folder structure
  • Setup Suspense
  • Fetch data from REST

๐Ÿ“ฆ Create folder structure

๐Ÿ‘‰ Checkout first step

git checkout client-step-0

๐Ÿ‘ Install create-react-app

npm install --global create-react-app

๐Ÿ‘Œ Create client folder

create-react-app client

โญ๏ธ Setup Suspense

๐Ÿ‘‰ Add Suspense into your index.js ReactDom.render:

ReactDOM.render(
  <Suspense fallback="Loading...">
    <App />
  </Suspense>,
  document.getElementById("root")
);

๐Ÿ‘‰ Import Suspense:

import React, { Suspense } from "react";

๐Ÿ’ซ Fetch data from REST

๐Ÿ‘‰ Change your App component as follow:

function App() {
  const launchesPast = useFetch("https://api.spacex.land/rest/launches-past");
  return (
    <React.Fragment>
      {launchesPast.map(({ mission_name, details, links, rocket }) => (
        <div key={String(mission_name)}>
          <h3>
            ๐Ÿ›ฐ {mission_name} ๐Ÿš€ {rocket.name}
          </h3>
          <p>{details}</p>
          <img src={links.flickr_images[0]} width="200" />
        </div>
      ))}
    </React.Fragment>
  );
}

๐Ÿ‘‰ Import useFetch

import useFetch from "fetch-suspense";

๐Ÿ‘‰ Install fetch-suspense

cd client
npm install fetch-suspense

๐Ÿ‘ Install dependencies & run the client

npm install
npm start

๐Ÿ‘Œ Explore the Client http://localhost:3000

Step 1๏ธโƒฃ JS GraphQL Client

Summary

  • Setup GraphQL Client
  • Fetch data from GraphQL

๐ŸŒŸ Setup GraphQL Client

๐Ÿ‘‰ Create on index.js a new Apollo Client

const client = new ApolloClient({
  uri: "http://api.spacex.land/graphql"
});

๐Ÿ‘‰ Include ApolloProvider

ReactDOM.render(
  <ApolloProvider client={client}>
    <Suspense fallback="Loading...">
      <App />
    </Suspense>
  </ApolloProvider>,
  document.getElementById("root")
);

๐Ÿ‘ Import dependencies

import { ApolloProvider } from "react-apollo-hooks";
import ApolloClient from "apollo-boost";

๐Ÿ‘Œ Install dependencies

npm install react-apollo-hooks apollo-boost graphql

โšก๏ธ Fetch data from GraphQL

๐Ÿ‘‰ Write on 'App.js' the GraphQL query

const query = gql`
  query getLaunches {
    launchesPast {
      mission_name
      details
      links {
        flickr_images
      }
      rocket {
        name: rocket_name
      }
    }
  }
`;

๐Ÿ‘‰ Add useQuery to fetch data from the GraphQL API

const launchesPastRest = useFetch("https://api.spacex.land/rest/launches-past");
const {
  data: { launchesPast = [] }
} = useQuery(query);

๐Ÿ‘‰ Import useQuery

import { useQuery } from "react-apollo-hooks";
import gql from "graphql-tag";

๐Ÿ‘‰ Run the client again

npm start

๐Ÿ‘ Explore the Client http://localhost:3000

๐Ÿ‘Œ Open your browser inspector tool and give a ๐Ÿ‘€ to both size & time GraphQL-REST calls!

Commit your changes! It's time to finish the workshop off ๐Ÿ’ช

Step 2๏ธโƒฃ TS GraphQL Client

Summary

  • Evolve the Client
  • Generate TS types
  • Evolve Safely the Client

๐ŸŽธ Evolve the Client

๐Ÿ‘‰ Checkout last step

git checkout client-step-2

๐Ÿ‘‰ Include launch_success field into our query

const query = gql`
  query getLaunches {
    launchesPast {
      mission_name
      details
      links {
        flickr_images
      }
      rocket {
        name: rocket_name
      }
      launch_success
    }
  }
`;

๐Ÿ‘‰ Remove useFetch call and log the query to verify that launch_success is there

function App() {
  // const launchesPast = useFetch("https://api.spacex.land/rest/launches-past");
  const {
    data: { launchesPast }
  } = useQuery(query);

  console.log("launchesPast", launchesPast)

  return (...);
}

๐Ÿ‘ Display launch_success

function App() {
  const {
    data: { launchesPast = [] }
  } = useQuery(query);
  return (
    <React.Fragment>
      {launchesPast.map(
        ({ mission_name, details, links, rocket, launch_success }) => (
          <div key={String(mission_name)}>
            <h3>
              ๐Ÿ›ฐ {mission_name} ๐Ÿš€ {rocket.
              }
            </h3>
            <p>{details}</p>
            <h3>Success: {launch_success ? "โœ…" : "โŒ"}</h3>
            <img src={links.flickr_images[0]} width="200" />
          </div>
        )
      )}
    </React.Fragment>
  );
}

๐Ÿ‘Œ Explore the Client to see the new field http://localhost:3000

๐ŸŽถ Generate TS types

๐Ÿ‘‰ Create codegen.yml file

touch codegen.yml

๐Ÿ‘‰ Include the configuration

schema: https://api.spacex.land/graphql
documents:
  - src/**/*.tsx
overwrite: true
watch: true
generates:
  ./src/types/types.d.ts:
    plugins:
      - typescript-common
      - typescript-client

๐Ÿ‘‰ Install codegen dependencies

๐Ÿ‘‰ Install dependencies

npm install

๐Ÿ‘ Open a terminal and run

npm start

๐Ÿ‘Œ Explore the Client http://localhost:3000

๐Ÿ‘ Open another terminal and run

npm run generate

๐Ÿ‘Œ Explore types/types.d.ts file

Fantastic, we are almost done. Don't forget to commit the changes!

๐ŸŽป Evolve Safely the Client

๐Ÿ‘‰ Type your returned data

const {
  data: { launchesPast }
} = useQuery<GetLaunches.Query>(query);

๐Ÿ‘‰ Import GetLaunches type definition

import { GetLaunches } from "../types";

๐Ÿ‘ Add the ships field to the query

const query = gql`
  query getLaunches {
    launchesPast {
      # ...
      ships {
        id
        name
        port: home_port
        image
      }
    }
  }
`;

๐Ÿ‘Œ Display the ships with the help of TypeScript auto-completion!

return (
  <React.Fragment>
    {launchesPast.map(
      ({ ..., ships }) => (
          // ...
          {ships.map(({ id, name, port, image }) => (
            <div key={id}>
              <h3>
                โ›ด {name} ๐ŸŒŠ {port}
              </h3>
              <img src={image} alt="" width={200} />
            </div>
          ))}
        </div>
      )
    )}
  </React.Fragment>
);

Step ๐Ÿ‘ฝ Introspect API from the IDE

๐Ÿ‘‰ Install Apollo GraphQL VS Code extension

๐Ÿ‘ Create apollo.config.js file:

module.exports = {
  client: {
    service: {
      url: "https://api.spacex.land/graphql"
    }
  }
};

๐Ÿ‘Œ Press Ctrl + Space Bar inside your query ๐Ÿคฏ

๐ŸŽŠ You're just finished all GraphQL Client steps, hoping that you've enjoyed & learned something new!

๐Ÿ˜„ Thanks for coming

Don't hesitate to contact @swcarlosrj if you'd have any question!

e2e-ts-gql-workshop's People

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.