In this section, you’re going to implement signup and login functionality that allows your users to authenticate against your GraphQL server.
User
modelThe first thing you need is a way to represent user data in the database. To do so, you can add a User
type to your Prisma data model.
You’ll also want to add a relation between the User
and the existing Link
type to express that Link
s are posted by User
s.
Now you see even more how Prisma helps you to reason about your data in a way that is more aligned with how it is represented in the underlying database.
Notice how you’re adding a new relation field called postedBy
to the Link
model that points to a User
instance. The User
model then has a links
field that’s a list of
Link
s.
To do this, you need to also define the relation by annotating the postedBy
field with
the @relation
attribute. This is required for every relation field in
your Prisma schema, and all you’re doing is defining what the foreign key of the related table will be. So in this case, we’re adding an extra field to store the id
of the User
who posts a Link
, and then telling Prisma that postedById
will be equal to the id
field in the User
table (if you are familiar with SQL, this kind of relation is being represented as one-to-many).
If this is quite new to you, don’t worry! We’re going to be adding a few of these relational fields and you’ll get the hang of it as you go! For a deeper dive on relations with Prisma, check out these docs.
This is a great time to refresh your memory on the workflow we described for your project at the end of chapter 4!
After every change you make to the data model, you need to migrate your database and then re-generate Prisma Client.
This command has now generated your second migration inside of prisma/migrations
, and you can start to see how this becomes a historical record of how your database evolves over
time. This script also run the Prisma migration, so your new models and types are ready-to-use.
That might feel like a lot of steps, but the workflow will become automatic by the end of this tutorial!
Your database is ready and Prisma Client is now updated to expose all the CRUD queries for the newly added User
model – woohoo! 🎉
Remember back when we were setting up your GraphQL server and discussed the process of schema-driven development? It all starts with extending your schema definition with the new
operations that you want to add to the API - in this case a signup
and login
mutation.
The signup
and login
mutations behave very similarly: both return information about the User
who’s signing up (or logging in) as well as a token
which can be used to
authenticate subsequent requests against your GraphQL API. This information is bundled in the AuthPayload
type.
After extending the schema definition with the new operations, you need to implement resolver functions for them.
In this tutorial, you will implement simple, naive implementation of a JWT (Json Web Token) implementation. This is a simple solution for creating token-based authentication.
You’ll also use bcryptjs
for simple encryption for the user’s password.
Let’s use the good ol’ numbered comments again to understand what’s going on here – starting with signup
.
signup
mutation, the first thing to do is encrypt the User
’s password using the bcryptjs
library which you’ll install soon.PrismaClient
instance (via prisma
as we covered in the steps about context
) to store the new User
record in the database.APP_SECRET
. You still need to create this APP_SECRET
and also install the jwt
library that’s used here.token
and the user
in an object that adheres to the shape of an AuthPayload
object from your GraphQL schema.You can now open GraphiQL and play around with your new schema and resolvers, try to run the following mutations:
mutation {
signup(email: "test@mail.com", name: "Dotan Simha", password: "123456") {
token
user {
id
name
email
}
}
}
Now, the login
mutation, add it under the signup
resolvers.
And if you’ll open Playground, you should be able to login with the user you previously created:
mutation {
login(email: "test@mail.com", password: "123456") {
token
user {
id
name
email
}
}
}
You should be able to get the information of the user. Please save the authentication token you get, we’ll need that on the next step!
Now on the login
mutation!
User
object, you’re now using your PrismaClient
instance to retrieve an existing User
record by the email
address that was sent along as an
argument in the login
mutation. If no User
with that email address was found, you’re returning a corresponding error.token
and user
again.Now, you have our users database ready to use, and our next step is to be able to detect who’s the current user that queries the server.
To do that, you’ll need to add the option to pass the authentication token along with our GraphQL operation.
You are not going to use the GraphQL schema in this case, since you don’t want to mix the authentication flow with the GraphQL contract that you have. So you’ll use HTTP headers.
The authentication token will be passed as a HTTP header, in the following form:
Authorization: "Bearer MY_TOKEN_HERE"
To add support for this kind of authentication in our server, you’ll need to be able to access the raw incoming HTTP request, then verify the token and identify the current user.
You also want to be able to tell who’s the current authenticated user during our resolvers, so you’ll inject the current user into the GraphQL context
.
So let’s do that:
So what happened here?
Authorization
for the incoming HTTP request headers.verify
of jsonwebtoken
to check that the token is valid, and extract the userId
from the token payload. null
in case of missing/invalid token.Now, every incoming GraphQL request that has a valid token and a user, will also have the context.currentUser
available with the authenticated user details. If an incoming request doesn’t have that, the context.currentUser
will be set to null
.
So that’s really cool, and to test that, you can add a new GraphQL field under type Query
called me
that just exposes the current user information.
You can now try it in GraphiQL with the following query:
query {
me {
id
name
}
}
And under the HEADERS
section of GraphiQL, add your authentication token in the following structure:
{
"Authorization": "Bearer YOUR_TOKEN_HERE"
}
And if you’ll run it, you’ll see that the GraphQL server now being able to authenticate you based on the token!
If you remember, you added more new fields to the GraphQL schema (such as Link.postedBy
), so let’s implement the missing resolvers!
To make sure our server knows how to identify the creator of each Link
, let’s modify the resolver of Mutation.post
to ensure that only authenticated users can use it, and also add the current authenticated user id to the object created on our database.
Let’s go and finish up the implementation, and connect everything together with the rest of the resolvers.
You can now try it from GraphiQL:
mutation {
post(url: "www.graphqlconf.org", description: "An awesome GraphQL conference") {
id
}
}
There’s one more thing you need to do before you can launch the GraphQL server again and test the new functionality: ensuring the relation between User
and Link
gets properly
resolved.
In the postedBy
resolver, you’re first fetching the Link
from the database using the prisma
instance and then invoke postedBy
on it. Notice that the resolver needs to be
called postedBy
because it resolves the postedBy
field from the Link
type in our type-definitions.
You can resolve the links
relation in a similar way.
That’s all! Now you have resolvers for all fields, and you can signup, login, identify the user as part of our GraphQL server!
You should be able to run complex GraphQL queries, for example:
query {
feed {
id
description
url
postedBy {
id
name
}
}
}