Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Nested Resources Example

This example demonstrates complex nested resource hierarchies with multiple path parameters, following a GitHub-like pattern for organizations, teams, members, and projects.

Features Demonstrated

Feature Description
Single path parameter /orgs/{org_id}
Two path parameters /orgs/{org_id}/teams/{team_id}
Three path parameters /orgs/{org_id}/teams/{team_id}/members/{member_id}
Path + query params /orgs/{org_id}/teams?include_private=true&page=1
Path + request body POST /orgs/{org_id}/teams with JSON body

Resource Hierarchy

Organization (Level 1)
    |
    +-- Team (Level 2)
          |
          +-- Member (Level 3)
          |
          +-- Project (Level 3)

Quick Start

# Generate code and run the server
make demo

# Test nested resource endpoints
make test

API Endpoints

Organizations (1 path parameter)

Method Endpoint Description
GET /api/v1/orgs List organizations
GET /api/v1/orgs/{org_id} Get organization
POST /api/v1/orgs Create organization

Teams (2 path parameters)

Method Endpoint Description
GET /api/v1/orgs/{org_id}/teams List teams
GET /api/v1/orgs/{org_id}/teams/{team_id} Get team
POST /api/v1/orgs/{org_id}/teams Create team

Members (3 path parameters)

Method Endpoint Description
GET /api/v1/orgs/{org_id}/teams/{team_id}/members List members
GET /api/v1/orgs/{org_id}/teams/{team_id}/members/{member_id} Get member
POST /api/v1/orgs/{org_id}/teams/{team_id}/members Add member
DELETE /api/v1/orgs/{org_id}/teams/{team_id}/members/{member_id} Remove member

Projects (3 path parameters)

Method Endpoint Description
GET /api/v1/orgs/{org_id}/teams/{team_id}/projects List projects
GET /api/v1/orgs/{org_id}/teams/{team_id}/projects/{project_id} Get project
POST /api/v1/orgs/{org_id}/teams/{team_id}/projects Create project

Example Requests

Get Organization (1 path parameter)

curl -X GET http://localhost:8080/api/v1/orgs/org-abc123 \
  -H "Authorization: Bearer test-token"

Get Team (2 path parameters)

curl -X GET http://localhost:8080/api/v1/orgs/org-abc123/teams/team-xyz789 \
  -H "Authorization: Bearer test-token"

Get Member (3 path parameters - deepest nesting)

curl -X GET http://localhost:8080/api/v1/orgs/org-abc123/teams/team-xyz789/members/member-456 \
  -H "Authorization: Bearer test-token"

List Teams with Query Parameters

curl -X GET "http://localhost:8080/api/v1/orgs/org-abc123/teams?page=1&limit=10&include_private=true" \
  -H "Authorization: Bearer test-token"

Create Team (path parameter + body)

curl -X POST http://localhost:8080/api/v1/orgs/org-abc123/teams \
  -H "Authorization: Bearer test-token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "New Team",
    "description": "A new team",
    "private": false
  }'

Add Member (2 path parameters + body)

curl -X POST http://localhost:8080/api/v1/orgs/org-abc123/teams/team-xyz789/members \
  -H "Authorization: Bearer test-token" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "user-new123",
    "role": 1
  }'

Remove Member (3 path parameters)

curl -X DELETE http://localhost:8080/api/v1/orgs/org-abc123/teams/team-xyz789/members/member-456 \
  -H "Authorization: Bearer test-token"

Proto Definition Pattern

Path parameters are automatically extracted from the URL and bound to request fields with matching names:

// Request with 3 path parameters
message GetTeamMemberRequest {
  string org_id = 1;     // Bound from {org_id} in path
  string team_id = 2;    // Bound from {team_id} in path
  string member_id = 3;  // Bound from {member_id} in path
}

// Service definition
service OrganizationService {
  rpc GetTeamMember(GetTeamMemberRequest) returns (Member) {
    option (sebuf.http.config) = {
      path: "/orgs/{org_id}/teams/{team_id}/members/{member_id}"
      method: HTTP_METHOD_GET
    };
  }
}

Combining Path and Query Parameters

message ListTeamMembersRequest {
  // From path
  string org_id = 1;
  string team_id = 2;

  // From query string
  int32 page = 3 [(sebuf.http.query) = { name: "page" }];
  int32 limit = 4 [(sebuf.http.query) = { name: "limit" }];
  string role_filter = 5 [(sebuf.http.query) = { name: "role" }];
}

Generated Files

After running make generate:

api/
  proto/
    models/
      organization.pb.go           # All message types
    services/
      organization_service.pb.go         # Service interface
      organization_service_http.pb.go    # HTTP handlers (12 endpoints!)
      organization_service_http_binding.pb.go  # Request binding
      organization_service_http_config.pb.go   # Server options
      organization_service_http_mock.pb.go     # Mock implementation
docs/
  OrganizationService.openapi.yaml  # OpenAPI with all nested paths
  OrganizationService.openapi.json

Key Concepts

Path Parameter Binding

Path parameters like {org_id} are automatically extracted from the URL and bound to the corresponding request message field. The field name must match the path variable name.

Hierarchical Resource Ownership

The nested URL structure enforces resource ownership:

  • A team belongs to an organization
  • A member/project belongs to a team within an organization

This is validated by including parent IDs in each request, allowing your service to verify ownership.

REST Best Practices

This example follows REST conventions:

  • Collection endpoints use plural nouns (/orgs, /teams, /members)
  • Single resource endpoints include the ID (/orgs/{org_id})
  • Nested resources show parent-child relationships in the URL
  • HTTP verbs match the operation (GET=read, POST=create, DELETE=remove)