Debug School

rakesh kumar
rakesh kumar

Posted on

How to transfer data efficiently using RPC

What is RPC (Remote Procedure Call)?
How RPC Works (Step-by-Step)
Difference Between REST and RPC (JSON-RPC, gRPC)
Programmatical advantage on RPC
How to implement RPC on LARAVEL

RPC (Remote Procedure Call)?
Remote Procedure Call (RPC) is a mechanism that allows programs to call functions/methods on a remote server as if they were local.

πŸ‘‰ Think of it as calling a function in another program running on a different machine. The RPC framework handles:

Data serialization (convert data into a format for transmission).
Network transport (send and receive requests over TCP, HTTP, etc.).
Request handling (execute function calls remotely).

Image description

How RPC Works (Step-by-Step)
1️⃣ Client Calls a Function

The client program calls a method (getUserDetails()).
2️⃣ RPC Framework Serializes Data
Converts the method name & parameters into JSON, Protobuf, or XML.
3️⃣ Request Sent to Server
Over HTTP, TCP, or WebSockets.
4️⃣ Server Processes the Request
Server receives, deserializes data, and executes getUserDetails().
5️⃣ Server Sends the Response
The response (user details) is sent back.
6️⃣ Client Receives & Deserializes Response
The response is converted back into a usable object.

Difference Between REST and RPC (JSON-RPC, gRPC)
1️⃣ What is REST API?
Uses HTTP methods (GET, POST, PUT, DELETE) with URLs.
Each resource has a specific endpoint (/users, /updatePassword).
Stateless (Each request is independent).
Best for public APIs (Easy to cache, debug).
βœ… Example: REST API
πŸ”Ή Server (Node.js + Express)

const express = require("express");
const app = express();
app.use(express.json());

app.post("/updatePassword", (req, res) => {
    const { email, password } = req.body;
    console.log(`Updating password for ${email}`);
    res.json({ success: true, message: "Password updated" });
});

app.listen(3000, () => console.log("REST API running on port 3000"));
πŸ”Ή Client (Flutter - Dart)
Enter fullscreen mode Exit fullscreen mode
import 'dart:convert';
import 'package:http/http.dart' as http;

class MotoshareAPI {
  String baseUrl = "http://localhost:3000";

  Future<void> updatePassword(String email, String password) async {
    var body = jsonEncode({"email": email, "password": password});

    final response = await http.post(
      Uri.parse("$baseUrl/updatePassword"),
      headers: {"Content-Type": "application/json"},
      body: body,
    );

    print("Response: ${response.body}");
  }
}
Enter fullscreen mode Exit fullscreen mode

βœ” Calls POST /updatePassword with JSON body.

What is JSON-RPC?
Remote Procedure Call (RPC): Calls a method (updatePassword) remotely.
Uses structured JSON requests instead of URLs.
Single API endpoint (/rpc) handles multiple methods.
Best for microservices, Web3, or private APIs.

Server (Python + Flask + JSON-RPC)

from flask import Flask, request, jsonify
from jsonrpcserver import method, dispatch

app = Flask(__name__)

@method
def updatePassword(email: str, password: str):
    print(f"Updating password for {email}")
    return {"success": True, "message": "Password updated"}

@app.route("/rpc", methods=["POST"])
def rpc():
    return jsonify(dispatch(request.get_data().decode())), 200

if __name__ == "__main__":
    app.run(port=5000)
πŸ”Ή Client (Flutter - Dart)
Enter fullscreen mode Exit fullscreen mode
import 'dart:convert';
import 'package:http/http.dart' as http;

class MotoshareAPI {
  String baseUrl = "http://localhost:5000/rpc"; // Single JSON-RPC endpoint

  Future<void> updatePassword(String email, String password) async {
    var body = jsonEncode({
      "jsonrpc": "2.0",
      "method": "updatePassword",
      "params": { "email": email, "password": password },
      "id": 1
    });

    final response = await http.post(
      Uri.parse(baseUrl),
      headers: {"Content-Type": "application/json"},
      body: body,
    );

    print("Response: ${response.body}");
  }
}
Enter fullscreen mode Exit fullscreen mode

Image description

Advantages of RPC over REST API
1️⃣ Simplicity in Calling Methods
πŸ”Ή RPC: You call remote methods as if they were local functions.
πŸ”Ή REST: You need to structure URLs, methods (GET, POST, PUT, DELETE), and handle HTTP responses.

Example:
βœ… RPC Call (JSON-RPC)

{
  "jsonrpc": "2.0",
  "method": "updatePassword",
  "params": { "email": "user@example.com", "password": "newPass123" },
  "id": 1
}
Enter fullscreen mode Exit fullscreen mode

βœ… REST API Call

{
  "email": "user@example.com",
  "password": "newPass123"
}
Enter fullscreen mode Exit fullscreen mode

But in REST, you also need:

Correct HTTP Method: (POST /updatePassword)
Headers: (Content-Type: application/json)
URL Encoding (for GET queries)

More Efficient Data Transfer (Especially with gRPC)
πŸ”Ή RPC (gRPC) uses binary Protocol Buffers (Protobuf), making it much faster than REST’s JSON.
πŸ”Ή REST uses HTTP 1.1 and JSON, which is slower due to text-based communication.

Image description

Programmatical advantage on RPC

Setting Up Laravel Backend (Server-Side)
We'll create a Laravel API that supports both JSON-RPC and REST API.

πŸ“Œ Step 1: Install Laravel

composer create-project --prefer-dist laravel/laravel motosharerpc
cd motosharerpc
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Step 2: Set Up Routes in routes/api.php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

// REST API Route
Route::put('/updatePassword', [UserController::class, 'updatePassword']);

// JSON-RPC Route
Route::post('/rpc', [UserController::class, 'handleRpc']);
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Step 3: Create UserController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class UserController extends Controller
{
    // πŸš€ REST API Method
    public function updatePassword(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required|email',
            'password' => 'required|min:6'
        ]);

        if ($validator->fails()) {
            return response()->json(['error' => $validator->errors()], 400);
        }

        $user = User::where('email', $request->email)->first();

        if (!$user) {
            return response()->json(['error' => 'User not found'], 404);
        }

        $user->password = Hash::make($request->password);
        $user->save();

        return response()->json(['message' => 'Password updated successfully']);
    }

    // πŸš€ JSON-RPC Method
    public function handleRpc(Request $request)
    {
        $data = $request->json()->all();
        if (!isset($data['method']) || $data['method'] !== 'updatePassword') {
            return response()->json([
                'jsonrpc' => '2.0',
                'error' => ['code' => -32601, 'message' => 'Method not found'],
                'id' => $data['id'] ?? null
            ]);
        }

        $params = $data['params'];
        $validator = Validator::make($params, [
            'email' => 'required|email',
            'password' => 'required|min:6'
        ]);

        if ($validator->fails()) {
            return response()->json([
                'jsonrpc' => '2.0',
                'error' => ['code' => -32602, 'message' => 'Invalid params'],
                'id' => $data['id']
            ]);
        }

        $user = User::where('email', $params['email'])->first();

        if (!$user) {
            return response()->json([
                'jsonrpc' => '2.0',
                'error' => ['code' => -32602, 'message' => 'User not found'],
                'id' => $data['id']
            ]);
        }

        $user->password = Hash::make($params['password']);
        $user->save();

        return response()->json([
            'jsonrpc' => '2.0',
            'result' => 'Password updated successfully',
            'id' => $data['id']
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ› οΈ 2️⃣ Flutter Frontend Implementation
We'll build a simple Flutter App that allows the user to update their password using both JSON-RPC and REST API.

πŸ“Œ Step 1: Add Dependencies to pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Step 2: Create network_service.dart
This file will handle API requests.

import 'dart:convert';
import 'package:http/http.dart' as http;

class NetworkService {
  final String baseUrl = "http://10.0.2.2:8000/api"; // Local Laravel Server

  // πŸš€ REST API Call
  Future<Map<String, dynamic>> updatePasswordRest(String email, String password) async {
    final response = await http.put(
      Uri.parse("$baseUrl/updatePassword"),
      headers: {"Content-Type": "application/json"},
      body: jsonEncode({"email": email, "password": password}),
    );

    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception("Failed to update password");
    }
  }

  // πŸš€ JSON-RPC Call
  Future<Map<String, dynamic>> updatePasswordJsonRpc(String email, String password) async {
    final response = await http.post(
      Uri.parse("$baseUrl/rpc"),
      headers: {"Content-Type": "application/json"},
      body: jsonEncode({
        "jsonrpc": "2.0",
        "method": "updatePassword",
        "params": {"email": email, "password": password},
        "id": 1
      }),
    );

    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception("JSON-RPC request failed");
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Step 3: Create main.dart (Flutter UI)

import 'package:flutter/material.dart';
import 'network_service.dart';

void main() {
  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    home: PasswordUpdateScreen(),
  ));
}

class PasswordUpdateScreen extends StatefulWidget {
  @override
  _PasswordUpdateScreenState createState() => _PasswordUpdateScreenState();
}

class _PasswordUpdateScreenState extends State<PasswordUpdateScreen> {
  final TextEditingController emailController = TextEditingController();
  final TextEditingController passwordController = TextEditingController();
  final NetworkService _networkService = NetworkService();
  String responseMessage = "";

  Future<void> updatePassword(bool useJsonRpc) async {
    try {
      final email = emailController.text;
      final password = passwordController.text;
      Map<String, dynamic> response;

      if (useJsonRpc) {
        response = await _networkService.updatePasswordJsonRpc(email, password);
      } else {
        response = await _networkService.updatePasswordRest(email, password);
      }

      setState(() {
        responseMessage = response.containsKey('result') ? response['result'] : response['message'];
      });
    } catch (e) {
      setState(() {
        responseMessage = "Error: $e";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Update Password")),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: emailController,
              decoration: InputDecoration(labelText: "Email"),
            ),
            TextField(
              controller: passwordController,
              decoration: InputDecoration(labelText: "New Password"),
              obscureText: true,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => updatePassword(false),
              child: Text("Update via REST API"),
            ),
            ElevatedButton(
              onPressed: () => updatePassword(true),
              child: Text("Update via JSON-RPC"),
            ),
            SizedBox(height: 20),
            Text(responseMessage, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Image description

JSON-RPC Request Example (Calling updatePassword)

{
  "jsonrpc": "2.0",
  "method": "updatePassword",
  "params": { "email": "user@example.com", "password": "newPass123" },
  "id": 1
}
Enter fullscreen mode Exit fullscreen mode

REST API Request Example (updatePassword)


{
  "email": "user@example.com",
  "password": "newPass123"
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)