Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Naming convention? #475

Closed
knweiss opened this issue Oct 13, 2017 · 5 comments
Closed

Naming convention? #475

knweiss opened this issue Oct 13, 2017 · 5 comments

Comments

@knweiss
Copy link

knweiss commented Oct 13, 2017

Is there a naming convention that maps HTTP methods + REST resource paths to gRPC procedure names?

I.e. how would you name the gRPC procedures of the following REST endpoints?

Method Resource Path Description
GET /users Get a list of all users
GET /users/42 Get a specific user id
GET /users/42/tickets Get all tickets of user id 42
GET /users/42/tickets/23 Get ticket id 23 of user id 42
POST /users Create a new user
POST /users/42/tickets Create new ticket for user id 42
PUT /users Bulk-update users
PUT /users/42 Update user id 42
PUT /users/42/tickets/23 Update ticket id 23 of user id 42
PATCH /users/42 Partially update user id 42
PATCH /users/42/tickets/23 Partially update ticket id 23 of user id 42
DELETE /users Delete all users
DELETE /users/42 Delete user id 42
DELETE /users/42/tickets/23 Delete ticket id 23 of user id 42

Here are some suggestions with alternatives...

GetAllUsers() or GetUsers() or ReadAllUsers() or ReadUsers()
GetUser(u) or ReadUser(u)
GetAllTicketsOfUser(u) or ReadAllTicketsOfUser(u)
GetTicketOfUser(t, u) or ReadTicketOfUser(t, u)
PostUser(u) or CreateUser(u)
PostTicketForUser(t, u) or CreateTicketForUser(t, u)
PutUsers(users) or BulkUpdateUsers(users) or UpdateUsers(users)
PutUser(u) or UpdateUser(u)
PutTicketForUser(t, u) or UpdateTicketOfUser(t,  u)
PatchUser(u) or ModifyUser(u)
PatchTicketOfUser(t, u) or ModifyTicketOfUser(t, u)
DeleteAllUsers() or DeleteUsers()
DeleteUser(u)
DeleteTicketOfUser(t, u)

I'm thinking about details like "All" in the procedure name, singular vs plural,
For vs Of, Get vs Read vs Retrieve, Update vs Modify, Delete vs Destroy, CRUD vs HTTP method names, etc. Any thoughts or preferences?

Of course the answer also depends on the programming language. I'm especially interested in Go.

Are the any conventions or best practices?

Maybe we can put the conclusion (together with rationales) into the FAQ?

@achew22
Copy link
Collaborator

achew22 commented Oct 18, 2017

I'm not aware of any naming rules beyond the style guide. Personally I use CRUDL as my prefix then the singular form of the resource in question and map the information from the query string into the request body for the rpc.

Here is what I would do (please forgive the pseudocode)

Method Resource Path Description RPC call example Note
GET /users Get a list of all users ListUser()
GET /users/42 Get a specific user id GetUser({id: 42})
GET /users/42/tickets Get all tickets of user id 42 GetTicket({userId: 42})
GET /users/42/tickets/23 Get ticket id 23 of user id 42 GetTicket({userId: 42, id: 23})
POST /users Create a new user CreateUser(userData)
POST /users/42/tickets Create new ticket for user id 42 UpdateUser(userData) Make sure to set id to 42 before sending this when you're not going through grpc gateway
PUT /users Bulk-update users BulkUpdateUser([]User)
PUT /users/42 Update user id 42 UpdateUser(userData)
PUT /users/42/tickets/23 Update ticket id 23 of user id 42 UpdateTicket(ticketDataThatIncludesTheUserId)
DELETE /users Delete all users DeleteUser({}) the empty object is the filter and with an empty object it will match everything
DELETE /users/42 Delete user id 42 DeleteUser({id:42})
DELETE /users/42/tickets/23 Delete ticket id 23 of user id 42 DeleteTicket({id:23, userId:42})

Note the omission of PATCH examples. I generally try not to implement patch. I've found it to be mostly an antifeature and very difficult to get right. Completely replacing the record is usually better.

One of the very nice features about this is that when you want to add a new endpoint it is often as simple as giving it the URL you would expect and it will generally just work! If you're interacting from gRPC directly then I would avoid REST semantics, after all you're writing an RPC API.

@knweiss
Copy link
Author

knweiss commented Oct 18, 2017

Thanks for your reply. That's what I was hoping for.

Personally, I don't used PATCH either. I was just adding it for completeness.

Question: How can you use two different RPCs with the same name but different arguments in gRPC (e.g. GetTickets() in your example)? When I try I always get an "is already defined" error from protoc.

The guessability aspect is what I had in mind when I was asking for a naming convention.

@knweiss
Copy link
Author

knweiss commented Oct 18, 2017

FWIW: In the meantime (i.e. before reading your reply) I thought some more about my own preference and came up with the following. The main difference is that I use more than one noun in the RPC names (e.g. to prevent the "is already defined" problem):

Method Resource Path Description gRPC call
GET /users Get a list of all users GetAllUsers()
GET /users/42 Get a specific user id GetUser(u)
GET /users/42/tickets Get all tickets of user id 42 GetAllTicketsOfUser()
GET /users/42/tickets/23 Get ticket id 23 of user id 42 GetTicketOfUser(t, u)
POST /users Create a new user CreateUser(u)
POST /users/42/tickets Create new ticket for user id 42 CreateTicketForUser(t, u)
PUT /users Bulk-update users BulkUpdateUsers(users)
PUT /users/42 Update user id 42 UpdateUser(u)
PUT /users/42/tickets/23 Update ticket id 23 of user id 42 UpdateTicketOfUser(t, u)
PATCH /users/42 Partially update user id 42 ModifyUser(u)
PATCH /users/42/tickets/23 Partially update ticket id 23 of user id 42 ModifyTicketOfUser(t, u)
DELETE /users Delete all users DeleteAllUsers()
DELETE /users/42 Delete user id 42 DeleteUser(u)
DELETE /users/42/tickets/23 Delete ticket id 23 of user id 42 DeleteTicketOfUser(t, u)

@achew22
Copy link
Collaborator

achew22 commented Oct 18, 2017

The thing I was trying to get at in my comment above is that conceptually DeleteTicketOfUser and DeleteTicket are the same thing. One is filtering by Id = 23 and the other is filtering by both id = 23 and user = 42. Due to that you can use a single method DeleteTicket to implement both pieces of functionality and just take params that are mappable into a REST namespace

@achew22
Copy link
Collaborator

achew22 commented Dec 9, 2017

I'm going to close this since there hasn't been movement ina while. Please reopen if you have more questions.

@achew22 achew22 closed this as completed Dec 9, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants