You’re reading the v0.1 development preview. APIs may change.
~ 4 min readUpdated 2026-10-29

React, Next.js, and other frontends

Elang can serve HTML or act as an API backend for React, Next.js, Vue, native mobile applications, and server clients. The official website uses HTML, CSS, and vanilla JavaScript. Your application can choose its own frontend.

Start with the API application

composer create-project --repository=https://elangphp.com/packages elang/api my-api 0.1.0-dev
cd my-api
php elang serve

Register an account, verify it using the local Studio mailbox, and create a token in /dashboard. Grant only the scopes your client needs. The working API includes private notes, validation, ownership checks, pagination, SSE, WebSocket capabilities, and /openapi.json.

Allow your frontend origin

Set the exact frontend origin in .env:

APP_URL=http://127.0.0.1:8000
FRONTEND_URL=http://localhost:3000

The API starter's CORS middleware allows the listed origin and handles preflight requests. Multiple origins can be comma-separated. Avoid wildcards. It supports Authorization, Content-Type, Idempotency-Key, Last-Event-ID, and Accept headers. Tokens are sent in Authorization; cross-origin session cookies are not enabled. CORS controls browser access and does not replace authentication.

Use fetch from any JavaScript framework

const response = await fetch('http://127.0.0.1:8000/api/notes', {
  headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { data, meta } = await response.json();

Use the pagination fields returned by the API. Handle 401 for expired/revoked tokens, 403 for insufficient scope, 404 for missing or another user's records, 422 for field errors, and 429 for rate limits.

Use the included client with React

Copy my-api/public/assets/elang-client.js into your frontend's src/lib/ directory. It is a dependency-free ES module using standard fetch. It is included in the Composer starter; no unpublished npm package is required.

import { useEffect, useState } from 'react';
import { createClient } from './lib/elang-client.js';

export function Notes({ token }) {
  const [notes, setNotes] = useState([]);
  const [error, setError] = useState('');
  useEffect(() => {
    const controller = new AbortController();
    const api = createClient({
      baseUrl: 'http://127.0.0.1:8000',
      getToken: () => token,
    });
    api.request('GET', '/api/notes', { signal: controller.signal })
      .then(result => setNotes(result.data))
      .catch(error => { if (error.name !== 'AbortError') setError(error.message); });
    return () => controller.abort();
  }, [token]);
  if (error) return <p role="alert">{error}</p>;
  return <ul>{notes.map(note => <li key={note.id}>{note.title}</li>)}</ul>;
}

The example receives a token from your application's sign-in integration. Do not hardcode a real token in source control, publish it in a bundle, or put it in a URL. The starter account UI creates personal API tokens; it is not an OAuth provider or a complete SPA login protocol.

Call Elang from Next.js on the server

Keep service credentials on the server. For user-specific applications, resolve a per-user credential from your server-side session instead of sharing one service token across users.

import 'server-only';

export default async function Page() {
  const response = await fetch(`${process.env.ELANG_API_URL}/api/notes`, {
    headers: { Authorization: `Bearer ${process.env.ELANG_API_TOKEN}` },
    cache: 'no-store',
  });
  if (!response.ok) throw new Error(`Elang returned HTTP ${response.status}`);
  const { data } = await response.json();
  return <ul>{data.map(note => <li key={note.id}>{note.title}</li>)}</ul>;
}

Place ELANG_API_URL and ELANG_API_TOKEN in your server deployment's secrets. Do not prefix the token variable with NEXT_PUBLIC_. Server-to-server fetch does not require CORS. An API token accesses its owner's records only.

Retry writes safely

const key = crypto.randomUUID();
const created = await api.request('POST', '/api/notes', {
  json: { title: 'A durable note', body: 'Created from React' },
  idempotencyKey: key,
});

Reuse the same key only when retrying the same operation with identical input. The API starter commits the buffered JSON response, domain write, and event on the same database connection. A repeated matching request returns the saved response; different input with the same key returns 409. External services need their own idempotency handling. This middleware is opt-in when no header is supplied and does not claim exactly-once delivery across systems.

Continue with real-time connections and large application architecture.

Need a hand?

Check the troubleshooting guide, look up a term in the glossary, or work through the complete task board project.

Search Elang documentation

Start typing to find your next step.