In this video, we will learn how to use gRPC’s optional parameters to build an update-user API in Golang.
- Join us on Discord: bit.ly/techschooldc
- Get the course on Udemy: bit.ly/backendudemy
- Buy us a coffee: www.buymeacoffee.com/techschool
- Full series playlist: bit.ly/backendmaster
- GitHub repository: github.com/techschool/simplebank
In this backend master class, we’re going to learn everything about how to design, develop, and deploy a complete backend system from scratch using PostgreSQL, Golang, and Docker.
TECH SCHOOL - From noob to pro
At Tech School, we believe that everyone deserves a good and free education. We create high-quality courses and tutorials in Information Technology. If you like the videos, please feel free to share and subscribe to support the channel or buy us a coffee: www.buymeacoffee.com/techschool
Hello, everyone, welcome back to the backend master class! In this lecture.
We will learn how to use gRPC’s optional parameters to build an update-user API.
As you’ve, seen in the previous video, We can use the SQLC’s null argument and COALESCE function.
To partially update some fields of the user record in the database.
So today, let’s write a new gRPC endpoint to allow the front end to update users’ information.
The first step would be writing the proto file for this new API.
It would be very similar to the create user.
So, I’m gonna copy the content of this proto file to a new file called rpc_update_user.proto Alright.
Now let’s open the new file in visual studio, code.
In this file.
We will define the request and response: messages.
I’m gonna change their names to UpdateUserRequest and UpdateUserResponse.
Now, for the request.
Its username should be a mandatory field, While, the full name, email, and password can be optional.
The reason is that users might want to update only 1 or 2 out of those 3 fields.
How can we tell protoc about this? Well, since protobuf version 3.15,? We can use the optional keyword.
To tell protoc that this field is not mandatory., So let’s.
Add it to all 3 optional fields:, full name, email, and password.
We can keep the response message: the same, as it will return the updated user object.
We have to add a new RPC to the simple bank, service.
So I’m gonna, open this service, proto file, And import, the rpc_update_user.proto file we’ve, just written.
Then, inside the SimpleBank service, I’m gonna duplicate the RPC CreateUser, Change, its name to UpdateUser, its input to UpdateUserRequest, and its output to UpdateUserResponse.
Now, since we’re using gRPC gateway in our server to convert between gRPC and HTTP requests, I’m gonna change this route to Patch /v1/update_user.
And finally, set the correct value for the description and summary of the swagger documentation.
One thing we should do every time we make changes to the service is to bump its version.
This will help us easily track.
What version is currently running on production.
Alright, now it’s time to generate the code.
But first, let’s check the current protoc version.
It’s 3.19 on my side.
You should make sure that your protoc version is at least 3.15 If you’re on a mac.
It can be easily updated using the “brew upgrade protobuf” command.
So now my protoc has been updated to the latest.
Version: 3.21 Let’s run “make proto” to generate Golang codes.
Now, if we look into the pb folder, There’s, a new file called rpc_update_user.pb.go It contains the codes that protoc generated for us.
You can notice that it has the UpdateUserRequest struct, And in this struct.
The type of the 3 optional fields is a pointer to string, Not, just a normal string.
As the mandatory field, username.
If we scroll down a bit, We, will see some methods of the struct to get its internal data.
For the username.
It just checks whether the request x is nil before returning x.username.
But for other optional fields like full, name, email, or password, Besides checking if x is nil.
It also checks whether the corresponding pointer is nil or not.
If it’s not nil, then the value stored under the pointer will be returned.
With this simple check, we will be able to tell whether the client wants to change the field’s value or not.
Now we will learn how to use the generated code.
To implement the update-user API in our gRPC server.
It should be very similar to the create-user RPC that we implemented in the gapi folder.
So let’s copy the content of this rpc_create_user.go file and rename it to rpc_update_user.go OK, back to visual studio, code, In, this new file, I’m gonna press command shift L to change all occurrences of the CreateUser word to UpdateUser.
We can keep most of the codes the same, First we validate the update user request, If violations is not nil.
We return an invalid argument.
You can watch lecture 47 to understand how it’s implemented.
Then here we’re hashing the input password because we don’t want to store its plaintext value in the db.
Note that we don’t have to do this.
If the password is not provided.
But I’ll deal with that later.
For now, let’s see what needs to be changed in the UpdateUserParams object! Remember that HashedPassword, FullName and Email are all optional, That’s why their data type is not string, but NullString.
Struct instead., So I’m gonna delete them, Then let’s, redeclare FullName, as a sql.NullString object.
Its string value should be set to req.GetFullName().
Its valid field is only true if the req.FullName is not nil.
Which means, its value is really provided by the client.
Now let’s duplicate this chunk of codes, And change, the field name to Email, Its string value to req.GetEmail(), And its valid field.
To: req.Email is not nil.
The last field we have to update, is hashed password.
I’m gonna cut this piece of code from here, Then.
After declaring the update user params, I’m gonna check.
If req.Password is not nil, Only in this case, then we will paste in the code to hash the new input.
And we will set arg.HashedPassword to a new sql.NullString object, Whose string value should be the new hashed password, And valid field should be true, since we already checked that password is not nil.
So now the update user argument is ready.
We can call server.store.UpdateUser with that object.
This call returns a not nil.
Error, We don’t have to check if it’s unique_violation like in the create user API, Because in this update user API, we don’t update the username.
If the username doesn’t exist, we will get a sql.ErrNoRows error.
In that case, we should return an error with status code, NotFound, And, a message: saying: user, not found.
The error is something else.
Then we just return.
This internal error: failed to update user.
Finally, when no errors, occur, We, simply create the UpdateUserResponse object with the updated user.
Record, And return it to the client.
That should be it for the UpdateUser RPC.
One more thing: we need to do to make it fully.
Complete, That is, improving the way we validate the UpdateUserRequest, Because in this API.
There are 3 optional fields that can be nil.
We can keep the validate username the same, as it’s a mandatory field.
But for the password, since it’s, an optional field, We have to check if req.Password is not nil.
Before performing this validation.
The same thing should be done for the full name, And, the email parameter as well.
OK, now I think the API is ready., Let’s open the terminal and start the server.
The server is serving both gRPC and HTTP.
Requests, We can test it using any gRPC or HTTP client tool.
I’m gonna use Postman to send HTTP requests.
Since the Update User API has the same set of parameters as Create User API, I’m, gonna duplicate this request, and change its name to Update User.
Then, in the body of the request, Let’s just keep the username, and delete all other fields.
Try sending this request, which doesn’t update anything.
Oops, we’ve got a Method Not Allowed error.
That’s because I forgot to change the method from POST to PATCH.
Alright, let’s try again! This time.
We got another error: user, not found.
So, it seems to be working, But.
We have to create the user Alice first.
So, I’m gonna, open the Create User API, And.
Send this request to the server.
Now user Alice has been created.
Let’s go back to the Update User API, And.
Send the request: again! This time, the request is successful, and the user’s data doesn’t change, exactly as we expected.
Now, let’s try changing the full name of this user to “New Alice”, And resend, the request! Oops, this time we’ve got a field violations.
Error: Full name must contain only letters or spaces.
This is weird because it is indeed containing only letters and spaces.
So I guess there’s something wrong with our validation.
Let’s go back to the code, and open the ValidateFullName function.
Then let’s see what we have in the isValidFullName regular expression.
OK, I, see! Here.
It should be only a single backslash, followed by an s to represent a space character.
Alright, let’s save the file, And restart the server.
Now back to Postman, and resend the request.
This time, the request is successful, And.
The user’s full name has been updated to “New Alice”.
Other fields are still the same.
That’s the correct behavior.
Try to update its email to “email@example.com” And, send the request.
You can see.
Only the email field got updated.
Now, I’m gonna change, only the password to “new_secret” The request is also successful, But.
We don’t know if the password has been updated or not Since, for security reasons, the API doesn’t return, the value of the hashed_password field.
And, this password_changed_at field, doesn’t change, either.
This, is something we must fix.
Whenever the password changes.
We should update the time it changes as well.
So let’s get back to the code and fix it! Here in this UpdateUser handler function, If the password is not nil.
We also need to update the password_changed_at field.
We have to change the UpdateUser SQL query to include this field.
Let’s open the user.sql file! I’m, gonna duplicate this set hashed_password statement, Then change the field name to password_changed_at, As we’ve learned in previous lecture, We can use the COALESCE function together with SQLC’s nullable argument.
To tell the database whether we want to change the value of this password_changed_at field, Or.
We just want to keep its original value.
Now we can save the SQL file, And run “make sqlc” in the terminal to regenerate Golang code for it.
Note that every time we regenerate SQL codes, we should also run “make mock” to update the mock store as well.
Now the new code is generated.
Let’s go back to our UpdateUser handler function, Here, after setting arg.HashedPassword to the new value, We should update the PasswordChangedAt field as well.
Also a nullable field, that’s why its type is sql.NullTime.
So, I’m gonna set arg.PasswordChangedAt to a new sql.NullTime object, Where.
Its time value should be the current timestamp, And.
Its valid field should be set to true.
That’s, basically, it! Let’s save the code, and restart the server.
Now let’s resend this request in Postman., This time.
The value of the password_changed_at field has been updated.
How about we try to update all 3 fields at the same time? I’m gonna set the full name to Alice, The email to “firstname.lastname@example.org”, And, the password to “secret”.
Then, send the request one more time., Voilà! You can see that all the fields have been updated to the new values.
Today, we’ve learned how to implement the UpdateUser gRPC API using protocol buffer’s optional, parameters.
This API is not secured yet, So.
Anyone can call it to update any other user’s data.
We need to do next.
Is, To, add an authorization layer to protect this API, So that only the real owner of the user can update it.
And that’s exactly what we’re gonna do in the next video.
I hope you’ve learned something new and useful today.
Thanks a lot for watching! Happy learning, and see you in the next lecture!.
gRPC is a robust open-source RPC (Remote Procedure Call) framework used to build scalable and fast APIs. It allows the client and server applications to communicate transparently and develop connected systems. Many leading tech firms have adopted gRPC, such as Google, Netflix, Square, IBM, Cisco, & Dropbox.How to create a gRPC server? ›
- Define a service in a . ...
- Generate server and client code using the protocol buffer compiler.
- Create the server application, implementing the generated service interfaces and spawning the gRPC server.
gRPC heavily uses HTTP/2 features and no browser provides the level of control required over web requests to support a gRPC client.