Accessing currentUser in the API side
As our blog has evolved into a multi-million dollar enterprise, we find ourselves so busy counting our money that we no longer have the time to write actual blog posts! Let's hire some authors to write them for us.
What do we need to change to allow multiple users to create posts? Well, for one, we'll need to start associating a blog post to the author that wrote it so we can give them credit. We'll also want to display the name of the author when reading an article. Finally, we'll want to limit the list of blog posts that an author has access to edit to only their own: Alice shouldn't be able to make changes to Bob's articles.
Associating a Post to a User
Let's introduce a relationship between a Post
and a User
, AKA a foreign key. This is considered a one-to-many relationship (one User
has many Post
s), similar to the relationship we created earlier between a Post
and its associated Comment
s. Here's what our new schema will look like:
┌─────────────────────┐ ┌───────────┐
│ User │ │ Post │
├─────────────────────┤ ├───────────┤
│ id │───┐ │ id │
│ name │ │ │ title │
│ email │ │ │ body │
│ hashedPassword │ └──<│ userId │
│ ... │ │ createdAt │
└─────────────────────┘ └───────────┘
Making data changes like this will start becoming second nature soon:
- Add the new relationship the
schema.prisma
file - Migrate the database
- Generate/update SDLs and Services
Add the New Relationship to the Schema
First we'll add the new userId
field to Post
and the relation to User
:
model Post {
id Int @id @default(autoincrement())
title String
body String
comments Comment[]
user User @relation(fields: [userId], references: [id])
userId Int
createdAt DateTime @default(now())
}
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
hashedPassword String
salt String
resetToken String?
resetTokenExpiresAt DateTime?
roles String @default("moderator")
posts Post[]
}
We created a User model in Chapter 4 when we set up authentication for our blog. Redwood's setup auth dbAuth
command generated two files for us that manage authentication: the auth
file in api/src/lib/
, and the auth
file in api/src/functions/
. Both of these files use our PrismaClient directly to work with the User model, so we didn't need to set up an SDL or services for the User model.
If you followed our recommendation in the Intermission to use the Example repo, the User SDL and service is already added for you. If not, you'll need to add it yourself:
yarn rw g sdl User --no-crud
We'll comment out the sensitive fields of our GraphQL User type so there's no chance of them leaking:
- JavaScript
- TypeScript
type User {
...
# hashedPassword: String!
# salt: String!
# resetToken: String
# resetTokenExpiresAt: DateTime
}
type User {
...
# hashedPassword: String!
# salt: String!
# resetToken: String
# resetTokenExpiresAt: DateTime
}
Migrate the Database
Next, migrate the database to apply the changes (when given the option, name the migration something like "add userId to post"):
yarn rw prisma migrate dev
Whoops!
Similar to what happened when we added roles
to User
, We made userId
a required field, but we already have several posts in our development database. Since we don't have a default value for userId
defined, it's impossible to add this column to the database.
@default(1)
in the schema?This would get us past this problem, but could cause hard-to-track-down bugs in the future: if you ever forget to assign a post
to a user
, rather than fail it'll happily just set userId
to 1
, which may or may not even exist some day! It's best to take the extra time to do things The Right Way and avoid the quick hacks to get past an annoyance like this. Your future self will thank you!
Since we're in development, let's just blow away the database and start over:
yarn rw prisma migrate reset
If you started the second half the tutorial from the Redwood Tutorial repo you'll get an error after resetting the database—Prisma attempts to seed the database with a user and some posts to get you started, but the posts in that seed do not have the new required userId
field! Open up scripts/seed.js
and edit each post to add userId: 1
to each:
{
id: 1,
name: 'John Doe',
title: 'Welcome to the blog!',
body:
"I'm baby single- origin coffee kickstarter lo - fi paleo skateboard.Tumblr hashtag austin whatever DIY plaid knausgaard fanny pack messenger bag blog next level woke.Ethical bitters fixie freegan,helvetica pitchfork 90's tbh chillwave mustache godard subway tile ramps art party. Hammock sustainable twee yr bushwick disrupt unicorn, before they sold out direct trade chicharrones etsy polaroid hoodie. Gentrify offal hoodie fingerstache.",
userId: 1,
},
Now run yarn rw prisma migrate reset
and and...you'll get a different error. But that's okay, read on...
We've got an error here because running a database reset
doesn't also apply pending migrations. So we're trying to set a userId
where one doesn't exist in the database (it does exist in Prisma generated client libs though, so it thinks that there should be one, even if it doesn't exist in the database yet).
It may feel like we're stuck, but note that the database did reset successfully, it's just the seed that failed. So now let's migrate the database to add the new userId
to Post
, and then re-run the seed to populate the database, naming it something like "add userId to post":
yarn rw prisma migrate dev
And then the seed:
yarn rw prisma db seed
If you didn't start your codebase from the Redwood Tutorial repo then you'll now have no users or posts in the database. Go ahead and create a user by going to http://localhost:8910/signup but don't create any posts yet! Change the user's role to be "admin", either by using the console introduced in the previous page or by opening Prisma Studio and changing it directly in the database.
Add Fields to the SDL and Service
Let's think about where we want to show our new relationship. For now, probably just on the homepage and article page: we'll display the author of the post next to the title. That means we'll want to access the user from the post in a GraphQL query something like this:
post {
id
title
body
createdAt
user {
name
}
}
To enable this we'll need to make two modifications on the api side:
- Add the
user
field to theposts
SDL - Add a relation resolver for the
user
in theposts
service
Add User to Posts SDL
type Post {
id: Int!
title: String!
body: String!
createdAt: DateTime!
user: User!
}
We did not add user
or userId
to the CreatePostInput
or UpdatePostInput
types. Although we want to set a user on each newly created post, we don't want just anyone to do that via a GraphQL call! You could easily create or edit a post and assign it to someone else by just modifying the GraphQL payload. We'll save assigning the user to just the service, so it can't be manipulated by the outside world.
Here we're using User!
with an exclamation point because we know that every Post
will have an associated user to it—this field will never be null
.