What we'll be building?
We'll be building a Pokemon collectibles app. You can have users signup and a database of Pokemon to choose from. After a user collects a Pokemon, it's his. It's first come first serve.
For this, we'll need
- Authentication for users
- DB to store, users and Pokemon
- Pokemon data
- A mapping or a foreign key to track which Pokemon belongs to who
- Realtime notification system for tracking Pokemon collection
- Front-end in React.js
Luckily we have Rocketgraph which provides this functionality right out of the box. Let's leverage that to build this much faster.
π‘To read this article you need to have a basic understanding of React.js and PostgreSQL
Rocketgraph : A complete backend that beats Firebase and is open-source
A little background. Rocketgraph provides a complete backend. It comes with a Postgres DB, Hasura console to manage your Postgres and add GraphQL layer to your data, Authentication and Serverless functions.
So to sum it up we provide auth for your web apps, GraphQL for realtime things like messages/notifications/comments etc and Serverless functions for anything you want to offload. Deploy your serverless functions by running rgraph deploy <project name>
in your repo.
So what the hack is GraphQL?
GraphQL is a language specification by Meta to achieve realtime queries on your data by asking for exactly what you need. This is different from traditional API approach where the query is coded into the backend and front-end has very little control over what/how to ask for data.
Think of it like a JSON query. You ask for what data you want inn a json-like query and it will return exactly those fields.
In this article we'll leverage the power of GraphQL, React Apollo and Hasura to build a real-time system for rating and commenting on movies. We can use this same system to book movie tickets too.
TLDR Version
If you just want to see the code. Here is the code base for this article. Here you can see more examples. This is the open-source software behind Rocketgraph
Keep reading
Before you start
Signup on Rocketgraph and create a project. This will also spin up an AWS RDS Postgres DB that we'll use to store our pokemon data. Once it's complete,
Cool, let's start from the basics
Create a react project and develop the front-end. Forget about the backend, we'll add it later.
mkdir pokemon-app && cd pokemon-app
Next scaffold a basic react application.
npx create-react-app ./
Install react router to be able to navigate between pages. Install react-apollo and graphql for real-time stuff as mentioned above.
yarn add react-router-dom
yarn add @apollo/client graphql
Next add some Rocketgraph libraries
yarn add @rocketgraphql/react-apollo
yarn add @rocketgraphql/rocketgraph-js-sdk
Delete the redundant files such as logo from the project and update the index.js file to as follows:
// src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { Login } from "./components/login";
import { Signup } from "./components/signup";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import App from "./App";
import { RApolloProvider } from "@rocketgraphql/react-apollo";
import { auth } from "./utils/rockets";
ReactDOM.render(
<React.StrictMode>
<RApolloProvider auth={auth} gqlEndpoint="https://hasura-vlklxvo.rocketgraph.app/v1/graphql">
<Router>
<Routes>
<Route path="/login" element={<Login />}/>
<Route path="/signup" element={<Signup />}/>
<Route path="/" element={<App />} />
</Routes>
</Router>
</RApolloProvider>
</React.StrictMode>,
document.getElementById("root")
);
And replace your gqlEndpoint
value with that on your Hasura console here:
App.js
import './App.css';
import { gql, useMutation, useSubscription } from "@apollo/client";
const GET_TODOS = gql`
subscription {
pokemon(limit: 100, order_by: {id: asc}) {
id
identifier
user_id
}
}
`;
const COLLECT = gql`
mutation MyMutation($name: String!) {
update_pokemon(where: {identifier: {_eq: $name}}, _set: {identifier: $name}) {
affected_rows
}
}
`
const PokemonComponent = (poke, func) => {
return (
<>
<li key={poke.id}>{poke.identifier}
{
poke.user_id ?
" collected"
: <button onClick={() => {console.log(poke); func(poke.identifier)}}>collect</button>
}
</li>
</>
)
}
function App() {
const { data, loading } = useSubscription(GET_TODOS);
console.log("data, loading", data, loading);
const [collectPoke, { poke_mutate, poke_loading }] = useMutation(COLLECT);
const collectPokemon = (poke_name) => {
collectPoke({ variables: { name: poke_name} });
}
return (
<div className="App">
{!data || !data.pokemon || !data.pokemon.length ? (
"no data"
) : (
<ul id="todosList">
{data.pokemon.map((poke) => {
return PokemonComponent(poke, collectPokemon);
})}
</ul>
)}
</div>
);
}
export default App;
components/signup:
import React, { useState } from "react";
import { useNavigate } from 'react-router-dom';
import { auth } from "../utils/rockets";
export function Signup(props) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
// login
try {
await auth.register({email, password});
navigate("/")
} catch (error) {
alert("error signing up");
console.error(error);
return;
}
}
return (
<div>
<h1>Signup</h1>
<form onSubmit={handleSubmit}>
<input
id="inputUserEmail"
type="email"
placeholder="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
id="inputUserPassword"
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button id="signupButton">Signup</button>
</form>
</div>
);
}
Aaaand, we are done!!
You can host your app on Vercel, use tailwind to make it look more professional.
Top comments (5)
Hahahaha 2 years is also minutes π
It looks long because of the code and screenshots. But when you get down to it, it takes about half an hour.
I know I know, I was just kidding. What I meant was that anything that costs more than a minute can be done in minutes hehehe π
haha, you have a fair point though. People like concise numbers like "15 minutes". Guess I have to change the tagline haha.
Hahahaha no need, it made me take a read