- 1. Why Use REST with the Terminal?
- 2. Basic REST Route + Command Example
- 3. Authenticated Routes (Logged-in Users Only)
- 4. POST Requests (Actions, Not Just Reads)
- 5. Permissions & Security (Very Important)
- 6. Error Handling Patterns
- 7. Mapping REST Responses to Terminal Output
- 8. REST + Events Combination (Advanced)
- 9. Summary
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 routes →
permission_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
- sanitize 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:
- Register route in PHP (
register_rest_route) - Implement
callbackwith sanitization + permission checks - Call it from JS using
fetch - Print formatted results with
cointacted_social_terminal_append_line - Handle errors gracefully