4
\$\begingroup\$

I want to ask question about a better way for implementing single endpoint REST.

I want to build a rest API but I want it to only have single endpoint like: graphql, where you only have like: host/graphql.

I already doing this example in laravel where you need to put a key and method to go to a certain API.

The body look like this (example of calling product API with single endpoint).

Calling get all product:

 {
 "key":"product/all",
 "method":"GET"
 }

And this is the get one (product/{id}):

 {
 "key":"product",
 "method":"GET",
 "payload":"{\"id\":\"1\"}"
 }

This is also work fine with post, put and delete.

Here is the single endpoint code (I use php laravel as example because it fast to make API):

 public function bridge(Request $request)
 {
 $host = "http://127.0.0.1:8000/api/";
 if ($request->has("payload")) {
 $bounds = html_entity_decode($request->payload);
 $payload = json_decode($bounds, true);
 }
 if (strtoupper($request->method) == "POST") {
 try {
 $url = $host . $request->key;
 $request = Request::create($url, 'POST', []);
 $response = Route::dispatch($request);
 return $response;
 } catch (\Throwable $th) {
 $data["data"] = [];
 $data["success"] = false;
 $data["code"] = 500;
 $data["message"] = $th->getMessage();
 return $data;
 }
 } else if (strtoupper($request->method) == "PUT") {
 try {
 $url = $host . $request->key . '/' . $payload['id'];
 $request = Request::create($url, 'PUT', []);
 $response = Route::dispatch($request);
 return $response;
 } catch (\Throwable $th) {
 $data["data"] = [];
 $data["success"] = false;
 $data["code"] = 500;
 $data["message"] = $th->getMessage();
 return $data;
 }
 } else if (strtoupper($request->method) == "DELETE") {
 try {
 $url = $host . $request->key . '/' . $payload['id'];
 $request = Request::create($url, 'DELETE', []);
 $response = Route::dispatch($request);
 return $response;
 } catch (\Throwable $th) {
 $data["data"] = [];
 $data["success"] = false;
 $data["code"] = 500;
 $data["message"] = $th->getMessage();
 return $data;
 }
 } else {
 $url = $host . $request->key;
 try {
 if ($request->has("payload")) {
 $url = $host . $request->key . "/" . $payload['id'];
 }
 $request = Request::create($url, 'GET');
 $response = Route::dispatch($request);
 return $response;
 } catch (\Throwable $th) {
 $data["data"] = [];
 $data["success"] = false;
 $data["code"] = 500;
 $data["message"] = $th->getMessage();
 return $data;
 }
 }
 }

As you can see there. It is calling the API twice "bridge" and the "key" API.

The cons here:

  • call request twice;
  • you can only put one /{id} variable (or more if you declare it).

Pros:

  • it only one endpoint so it will be easy to make an api helper in the frontend (maybe XD).

Can you show me a better way of doing this? Tell me your tips and thought or maybe show some code (any language is fine).

mickmackusa
8,7921 gold badge17 silver badges31 bronze badges
asked Apr 4, 2021 at 17:47
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

I only have a few quibbling remarks:

  • Don't Repeat Yourself (D.R.Y.)
    • Declare $url = $host . $request->key unconditionally at the start of your method (say, after $host). Then append text to it if/when needed $url .= '/' . $payload['id'];.
    • strtoupper($request->method) is done in each conditional expression. It is better practice to mutate the string once. You're also checking the same data for different strings, this is what switch-case blocks are for.
    • Rather than writing $data (a non-descriptive variable name) over and over, omit the variable declaration entirely and write the data directly into the return. I regularly advise against single-use variable declarations unless they valuably reduce code width or improve readability.
  • else if should be elseif in PHP to comply with the PSR-12 coding standard.
  • All of your thrown exceptions are handled identically, so it seems to be a much cleaner choice to wrap your whole switch block in a single try-catch block.
  • Maybe you'd like to see the payload when there is a thrown exception; if so, use "data" => $payload ?? [] in the return.

Your code might be rewrite as:

public function bridge(Request $request)
{
 $host = "http://127.0.0.1:8000/api/";
 $url = $host . $request->key;
 if ($request->has("payload")) {
 $payload = json_decode(html_entity_decode($request->payload), true);
 }
 try {
 switch (strtoupper($request->method)) {
 case "POST":
 return Route::dispatch(Request::create($url, 'POST', []));
 case "PUT":
 $url .= '/' . $payload['id'];
 return Route::dispatch(Request::create($url, 'PUT', []));
 case "DELETE":
 $url .= '/' . $payload['id'];
 return Route::dispatch(Request::create($url, 'DELETE', []));
 default:
 if ($request->has("payload")) {
 $url .= "/" . $payload['id'];
 }
 return Route::dispatch(Request::create($url, 'GET'));
 }
 } catch (\Throwable $th) {
 return [
 "data" => $payload ?? [],
 "success" => false,
 "code" => 500,
 "message" => $th->getMessage(),
 ];
 }
}
answered Apr 4, 2021 at 23:11
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.