TJ is too short
TJ is too short

Reputation: 873

RESTful API: Data from multiple endpoints in one single view

I've started creating a RESTful API (well, I did my best, I'm trying to follow the patterns) and I have stumbled upon a scenario that I'm not really sure how to handle. I will explain the current structure:

My application has 4 controllers:

Taking as example the Customers controller, I have defined the following actions:

This is the full code of the Customer controller:

namespace App\Http\Controllers;

use App\Customer;
use Illuminate\Http\Request;

class CustomerController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return Customer::all();
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $customer = Customer::create($request->all());
        return response()->json($customer, 201);
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Customer  $customer
     * @return \Illuminate\Http\Response
     */
    public function show(Customer $customer)
    {
        return $customer;
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Customer  $customer
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Customer $customer)
    {
        $customer->update($request->all());
        return response()->json($customer, 200);

    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Customer  $customer
     * @return \Illuminate\Http\Response
     */
    public function destroy(Customer $customer)
    {
        $customer->delete();
        return response()->json(null, 204);
    }
}

The code is very similar in the other controllers. It's also important to note that:

The problem starts here:

I need to display in the front-end a summary page with all customer data (name, email, registration date, etc) and a box showing the number of payments made and another box showing the number of entries in the Log.

Do I need to make 3 requests? (One to /customers/id, other to customers/id/payments and other to customers/id/logs)

If I return all the customer related data in the customers/id call, am I breaking the RESTful convention?

Upvotes: 1

Views: 3177

Answers (2)

inf3rno
inf3rno

Reputation: 26129

I think your problem that you confuse your REST API with your database. They don't have to follow the same structure. You can easily return the whole nested JSON for GET /customers/{id} if that's what you need from your REST API.

To get flexibility, you can also allow your GET /customers/{id} to read relations param and return the customer along with loaded relations as specified in the request. Like so:

public function show(Request $request, Customer $customer)
{
    $validated = $request->validate(['relations' => ['nullable', Rule::in([<list of relations you want to honor/allow here>])];
    $customer->load($validated['relations']);

    return $customer;
}

Upvotes: 1

webprogrammer
webprogrammer

Reputation: 2477

I am using apigility, but my answer still will be related to your question. According to the REST terminology (which could be find here https://apigility.org/documentation/intro/first-rest-service#terminology ) You are talking about entity and collection.

/customers/id - entity,
/customers/id/payments - collection,
/customers/id/logs - collection. 

These are 3 different requests. So, yes, you need make 3 different requests.

But, to be honest, if you don't need pagination over payments and logs you can have only one request to /customers/id and within response you can have fields with array

{
    "_links": {
        "self": {
            "href": "http://localhost:8080/status/3c10c391-f56c-4d04-a889-bd1bd8f746f0"
        }
    },
    "id": "3c10c391-f56c-4d04-a889-bd1bd8f746f0",
    ...
    _payments: [
        ...
    ],
    _logs: [
        ...
    ],
}

Upd (duplicate from comment for future visitors).

Also, you should pay attention to DTO. I suppose this link will be interesting https://stackoverflow.com/a/36175349/1581741 .

Upd2.

At current moment I treat your collection /customers/id/payments like this:

/payments?user_id=123

where user_id is filtering field on payments table.

Upvotes: 2

Related Questions