View Categories

REST API Integration Guide

5 min read

This article explains how to connect terminal commands to your WordPress backend using the REST API.

You’ll learn how to:

  • create custom REST routes in PHP
  • call them from terminal commands in JS
  • handle permissions
  • return data and show it in the terminal

This is the standard, safe way to let commands talk to the server.


1. Why Use REST with the Terminal? #

Because:

  • commands run in the browser (JS only)
  • sensitive logic and data must stay on the server
  • REST is the clean, WordPress-native way to bridge them

Use REST when your command needs:

  • database data
  • user-specific info
  • secure actions
  • integration with other plugins
  • anything that shouldn’t be done purely in the browser

2. Basic REST Route + Command Example #

Step 1 — Register a REST route (PHP) #

In your add-on or custom plugin:

add_action('rest_api_init', function () {
    register_rest_route('cst/v1', '/ping', [
        'methods'  => 'GET',
        'callback' => function () {
            return [
                'status'  => 'ok',
                'message' => 'Pong from REST!'
            ];
        },
        'permission_callback' => '__return_true', // public
    ]);
});

This creates:

GET /wp-json/cst/v1/ping


Step 2 — Create a terminal command that calls it (JS) #

CointactedSocialTerminalTerminal.registry.restping = async function () {
    cointacted_social_terminal_append_line("Contacting server…", "system");

    try {
        const res = await fetch('/wp-json/cst/v1/ping');
        const json = await res.json();

        cointacted_social_terminal_append_line(
            `${json.status.toUpperCase()}: ${json.message}`,
            "success"
        );
    } catch (e) {
        cointacted_social_terminal_append_line("REST error.", "error");
    }
};

CointactedSocialTerminalTerminal.commands_meta.restping = {
    group: "API",
    description: "Ping the server via REST."
};

Usage:

restping

3. Authenticated Routes (Logged-in Users Only) #

If the command needs user-specific data (e.g. points, profile info, private stuff), use a permission callback.

PHP — Auth-only route #

add_action('rest_api_init', function () {
    register_rest_route('cst/v1', '/profile', [
        'methods'  => 'GET',
        'callback' => function (\WP_REST_Request $request) {
            $user_id = get_current_user_id();

            if (! $user_id) {
                return new \WP_Error('not_logged_in', 'You must be logged in.', ['status' => 401]);
            }

            $user = wp_get_current_user();

            return [
                'id'    => $user->ID,
                'name'  => $user->display_name,
                'email' => $user->user_email,
            ];
        },
        'permission_callback' => function () {
            return is_user_logged_in();
        },
    ]);
});

JS — Command that calls it #

CointactedSocialTerminalTerminal.registry.myprofile = async function () {
    cointacted_social_terminal_append_line("Loading profile…", "system");

    try {
        const res = await fetch('/wp-json/cst/v1/profile', {
            credentials: 'include'
        });

        if (!res.ok) {
            return cointacted_social_terminal_append_line("You must be logged in.", "error");
        }

        const json = await res.json();

        cointacted_social_terminal_append_line(`ID: ${json.id}`, "system");
        cointacted_social_terminal_append_line(`Name: ${json.name}`, "system");
        cointacted_social_terminal_append_line(`Email: ${json.email}`, "system");

    } catch (e) {
        cointacted_social_terminal_append_line("Profile request failed.", "error");
    }
};

CointactedSocialTerminalTerminal.commands_meta.myprofile = {
    group: "USER",
    description: "Show logged-in profile via REST."
};

Usage:

myprofile

4. POST Requests (Actions, Not Just Reads) #

Use POST when the command changes something.

PHP — POST route #

add_action('rest_api_init', function () {
    register_rest_route('cst/v1', '/note', [
        'methods'  => 'POST',
        'callback' => function (\WP_REST_Request $request) {
            $user_id = get_current_user_id();
            if (! $user_id) {
                return new \WP_Error('not_logged_in', 'Login required.', ['status' => 401]);
            }

            $note = sanitize_text_field($request->get_param('note'));

            update_user_meta($user_id, 'cst_note', $note);

            return [
                'status' => 'saved',
                'note'   => $note,
            ];
        },
        'permission_callback' => function () {
            return is_user_logged_in();
        },
    ]);
});

JS — terminal command #

CointactedSocialTerminalTerminal.registry.savenote = async function (input) {
    const note = input.args.join(" ");

    if (!note) {
        return cointacted_social_terminal_append_line("Usage: savenote <text>", "error");
    }

    cointacted_social_terminal_append_line("Saving note…", "system");

    try {
        const res = await fetch('/wp-json/cst/v1/note', {
            method: 'POST',
            credentials: 'include',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ note })
        });

        if (!res.ok) {
            return cointacted_social_terminal_append_line("Failed to save note.", "error");
        }

        const json = await res.json();
        cointacted_social_terminal_append_line(`Saved: ${json.note}`, "success");
    } catch (e) {
        cointacted_social_terminal_append_line("Server error.", "error");
    }
};

CointactedSocialTerminalTerminal.commands_meta.savenote = {
    group: "TOOLS",
    description: "Save a personal note via REST."
};

5. Permissions & Security (Very Important) #

Quick rules:

  • Public routespermission_callback => '__return_true'
  • Logged-in only → use is_user_logged_in()
  • Role-based → check current_user_can('manage_options') or custom caps
  • Always:
    • sanitize input (sanitize_text_field, sanitize_email, etc.)
    • validate arguments
    • never trust user input

Example (admin-only route):

register_rest_route('cst/v1', '/admin-stats', [
    'methods'  => 'GET',
    'callback' => 'cst_admin_stats_callback',
    'permission_callback' => function () {
        return current_user_can('manage_options');
    },
]);

6. Error Handling Patterns #

On PHP side, return WP_Error for errors:

return new \WP_Error('my_error_code', 'Something went wrong.', ['status' => 400]);

On JS side, check res.ok and handle:

if (!res.ok) {
    cointacted_social_terminal_append_line("Server returned an error.", "error");
    return;
}

And optionally log details to console for developers.


7. Mapping REST Responses to Terminal Output #

Good pattern:

  • Keep REST response minimal & structured
  • Format nicely in the terminal

Example JSON:

{
  "balance": 120,
  "level": 3,
  "next_level_at": 200
}

Terminal command:

cointacted_social_terminal_append_line(`Balance: ${json.balance}`, "success");
cointacted_social_terminal_append_line(`Level: ${json.level}`, "system");
cointacted_social_terminal_append_line(`Next level at: ${json.next_level_at}`, "system");

8. REST + Events Combination (Advanced) #

You can combine:

  • command → REST
  • output event → tracking
document.addEventListener('cointacted_social_terminal_output', (e) => {
    if (e.detail.type === "error") {
        fetch('/wp-json/cst/v1/log-error', {
            method: 'POST',
            body: JSON.stringify(e.detail)
        });
    }
});

9. Summary #

Use REST when your commands need to:

✔ Read/write from WordPress #

✔ Access user data #

✔ Integrate with other plugins #

✔ Perform secure actions #

✔ Stay compatible with WordPress best practices #

The standard flow is:

  1. Register route in PHP (register_rest_route)
  2. Implement callback with sanitization + permission checks
  3. Call it from JS using fetch
  4. Print formatted results with cointacted_social_terminal_append_line
  5. Handle errors gracefully

Leave a Reply

Your email address will not be published. Required fields are marked *