Using APIs
Table of contents
- Overview
- Configuration
- Service: OpenWeather client (server-side)
- Controller: API endpoint
- Router & ACL
- Front End
- Test
1. Overview Table of Contents
We support RESTful API requests to API providers. In this guide we will build a tiny API endpoint that does server-side fetch to OpenWeatherMap (OWM) and returns safe JSON to your React app.
- Client calls:
GET /weather/show?q=Newport News, Virginia&units=imperial - Server fetches from OWM, caches the response (TTL), normalizes errors, and returns JSON.
- React renders a
WeatherCardusinguseAsync+apiGet.
Why proxy?
- You hide your OWM API key on the server.
- You control caching, validation, and error shape.
- You avoid CORS/key leakage issues.
2. Configuration Table of Contents
Add your OWM key to .env (server only):
OWM_API_KEY=your_openweather_key_here
(Do not expose this key to Vite or the browser.)
3. Service: OpenWeather client (server-side) Table of Contents
Run the command:
php console make:service WeatherService
New file is created at app\Services.
Implement the WeatherService class:
<?php
declare(strict_types=1);
namespace App\Services;
use Core\Lib\Utilities\Env;
use Core\Lib\Http\Api;
class WeatherService extends Api {
public function __construct()
{
parent::__construct(
baseUrl: 'https://api.openweathermap.org/data/2.5',
cacheNamespace: 'owm',
defaultHeaders: ['Accept' => 'application/json'],
defaultQuery: [
'appid' => Env::get('OWM_API_KEY'),
// Set a default; client can override via ?units=metric|imperial
'units' => 'imperial',
],
defaultTtl: 120, // seconds to cache GETs
timeout: 6
);
}
/**
* Current conditions by free-form query 'q' (e.g., city,country).
* Supports 'q', 'units', 'lang', or lat/lon parameters.
*/
public function current(array $query): array
{
// Allow q=, zip=, or lat/lon; pass through units/lang if present
$allowed = ['q', 'zip', 'lat', 'lon', 'units', 'lang'];
$params = array_intersect_key($query, array_flip($allowed));
return $this->get('/weather', $params); // cached via Api::get
}
}
Make sure you import Core\Lib\Http\Api and extend the Api class.
4. Controller: API endpoint Table of Contents
Create the controller:
php console make:controller Weather
Import App\Services\WeatherService and implement the showAction.
<?php
namespace App\Controllers;
use Throwable;
use Core\Controller;
use App\Services\WeatherService;
class WeatherController extends Controller
{
public function showAction()
{
try {
$q = $_GET['q'] ?? null;
$lat = $_GET['lat'] ?? null;
$lon = $_GET['lon'] ?? null;
if (!$q && !($lat && $lon)) {
return $this->jsonError('Provide ?q=City or ?lat=&lon=', 422);
}
$svc = new WeatherService();
$data = $svc->current($_GET);
$this->jsonResponse(['success' => true, 'data' => $data]);
} catch (Throwable $e) {
$this->jsonError('Upstream error', 502, ['detail' => $e->getMessage()]);
}
}
// OPTIONS /api/weather/* (CORS)
public function preflightAction(): void
{
$this->preflight();
}
}
5. Router & ACL Table of Contents
Router
Your router maps /{Controller}/{action}. Two common choices:
- *Option A (as written): call
GET /api/weather/show?q=... - Option B: make indexAction and call
GET /weather?q=...
Use whichever you prefer—your current style is fine.
ACL (app/acl.json) Let Guests read weather; restrict writes if needed.
{
"Guest": {
"Weather": ["show", "preflight"]
},
"LoggedIn": {
"Weather": ["show", "preflight"]
},
"denied": {}
}
6. Front End Table of Contents
Make a new component:
php console react:component WeatherCard
The file is created at resources\js\components\.
Import apiGet and useAsync from @chappy/utils/api then implement the WeatherCard.
import React from 'react';
import { apiGet, useAsync } from '@chappy/utils/api';
export default function WeatherCard({ city = 'Newport News, Virginia', units = 'imperial' }) {
const { data, loading, error } = useAsync(({ signal }) =>
apiGet('/weather/show', { query: { q: city, units }, signal }),
[city, units]);
if (loading) return <div>Loading…</div>;
if (error) return <div className="text-danger">{error.message}</div>;
const d = data?.data || {};
return (
<div className="card p-3">
<h5 className="mb-2">{d.name}</h5>
<div>
{Math.round(d.main?.temp)}°{units === 'metric' ? 'C' : 'F'} — {d.weather?.[0]?.description}
</div>
</div>
);
}
Mount from a PHP host view the same way you mount other React pages (via your app.jsx entry + data-component/data-props).
7. Test Table of Contents
Browser/React Open a page that renders WeatherCard.
cURL / Postman
curl "http://localhost:8000/api/weather/show?q=Austin,TX&units=imperial"
Example error
{
"success": false,
"message": "Provide ?q=City or ?lat=&lon=",
"errors": []
}