Piecing Together GraphQL

1,712 reads

Photo by simon peel on Unsplash

GraphQL’s concept spans from data store to client. What happens along the way? Tooling in the GraphQL world has come a long way and spinning up an application is easier than ever. By stepping through each component, you’ll have a better understanding of GraphQL as a whole.

To follow along with this tutorial, clone this fullstack example. It’s a bare-bones app using sessions to authenticate users, a battle tested and secure option for persisting user auth.

Our Stack

Image from https://www.howtographql.com

Looking at the image above from left to right, the first represents our client application built on Apollo (our GraphQL data cache) and React. I’m using Parcel to build the client bundle.

Next, the “GraphQL Server” will be graphql-yoga, a server based on Express which interfaces with Apollo and provides us with a lot of sensible defaults.

Prisma will stand between the server and the database as our data layer, similar to an ORM for writing and accessing data to and from our database. Prisma is open source and supports all of the major databases (the stack of pancake on the right of the image).

Setup

Start by installing some global packages.

yarn global add prisma graphql-cli nodemon

Create a .env file in our project folder, we’ll fill in the DB_URL variable once we deploy Prisma:

APP_SECRET=mysecret123

DB_URL=

Next, create a Prisma account then log into the Prisma CLI with prisma login so you can spin up free dev servers for your projects. Don’t worry, Prisma can be moved to the hardware of your choice at any time and won’t lock you in.

We’re going to generate our user models and deploy them to our test database. Inside of /database you’ll see we have one model, User . When we deploy, Prisma will create the bindings based on this schema. Our .graphqlconfig.yml in the root of our project will explain to Prisma how we want our setup deployed. Now, run:

prisma deploy database

Select “Demo server”, choose whichever region makes sense, enter a name like “fullstack” and set the stage as “dev”. Prisma will generate an endpoint like https://us1.prisma.sh/myusername-12345/fullstack/dev , drop it into your .env .

One last thing, Prisma will comment out your database/prisma.yml to:

...

So switch it back to:

endpoint: ${env:DB_URL}

...

How it’ll Work

After installing dependencies with yarn install or npm install fire it up your local server with npm run dev and navigate to localhost:1234 . The dev command will simultaneously run our server and frontend build.

Try Out

Sign up and Log in! After a user is successfully created, it will kick you to the dashboard.

Once you’re logged in, the protected routes won’t allow authenticated users to the /signup or /login routes. Once logged out, you can’t go to /dashboard .

Try refreshing while logged in. On page load the app will make a request to check user’s authentication state. If authenticated, the page will then fetch the user’s data.

The Backend

Open /src/index.js you’ll see the graphql-yoga server instantiated with GraphQLServer . It takes a number of arguments:

Typedefs: This is our GraphQL schema. Inside the file you’ll see we’ve listed the queries and mutations that will be handled by our resolvers.

Resolvers: These are the methods that do the work of logging in, fetching our user, etc. Their inputs and return values must match up with what’s defined in our typeDef’s schema.graphql .

Context: this takes a function that we’ll use to set the database to point to our Prisma set up. Pass in the generated typeDefs from the prisma deploy as well as the endpoint and secret from our .env .

As mentioned above, graphql-yoga is based on Express so we can use common packages like express-session to handle our sessions.

After we define our sessions implementation, we start the server with CORS settings that allow our frontend on localhost:1234 (via parcel) to get our session cookies.

The Client Side

Our front end is a barebones React application bundled up with Parcel. Starting in client/index.jsx we set up the ApolloClient to connect to our server running on port 4000 and assign the client to our ApolloProvider .

In the Header component you’ll see we’re calling the GET_AUTH_STATUS query which checks if there’s a session on the backend. It’ll determine whether to show the login / signup links or logout.

Further down in index.jsx you’ll see protected routes for our Dashboard and auth routes. I created a special route component, ProtectedRoute , which calls GET_AUTH_STATUS to decide whether to display the corresponding component. Even though we call our auth status query in a few different places, when you open “Network” in developer tools you’ll see that it is only requested once. This is because Apollo Cache handles the data between different components automatically.

The Auth component handles both our signup and login mutations. On successful login or signup, we need to update our GET_AUTH_STATUS because LOGIN and SIGNUP mutations only return the User object. Instead of making another request for auth status, we’ll just update the status manually:

<Mutation

mutation={isSignup ? SIGNUP : LOGIN}

variables={isSignup ? { email, name, password } : { email, password }}

update={(cache) => {

cache.writeQuery({

query: GET_AUTH_STATUS,

data: {

isLoggedIn: {

status: true,

__typename: 'AuthStatus',

},

},

});

}}

>

Wrap Up

Now’s a good time to dive in to GraphQL if you’ve been watching from the sidelines. The GraphQL eco-system has come a long way in the few years it’s been around. Each of the layers in the stack has matured enough that it’s relatively straightforward to build an app from top to bottom.

Also, interest is on the rise. The most recent “State of Javascript” reflects this sentiment and we’ll only see more in the future.

Why not store JWTs in local storage or cookies? Well, these articles can explain better than me, but suffice to say using server side sessions to save sensitive data is safer (and easy).

Again, here’s a link to the example repository.

Tags