rest_api_init only fires on a REST request, which is efficient and means anything registered there does not exist during a normal page load — including the route definitions something else may want to read.
// runs only on REST requests
add_action( 'rest_api_init', 'shop_register_routes' );
// the route definition itself should be a plain data structure,
// available to anything that asks
function shop_route_definitions() {
return array( /* ... */ );
}
Keeping the definitions as data and the registration as a thin wrapper means a WP-CLI command or a test can enumerate the routes without booting the REST server. The other consequence: a permission callback that depends on something set up on init is fine, because init has already run by then, but one depending on wp or the main query is not — neither has happened on a REST request.