Przejdź do treści
Intum Pomoc

Aplikacje Noe

Aktualizacja: 13 min czytania
Na tej stronie

Tworzenie, aktualizacja i uruchamianie aplikacji webowych Noe przez API.

Jak robimy aplikacje Noe (skrót)

  • Aplikacja to rekord z polem source_code w wybranym silniku: svelte (domyślny, Svelte 5 z $state), vue, react, js, html; CSS przez css_engine: "tailwind_all". Serwer sam kompiluje kod
  • url_code daje przyjazny adres (np. “pogoda” → /a/pogoda), public: true udostępnia apkę bez logowania
  • Dane trzymamy w bazie Noe (db_code + publiczne metody bazy) albo pobieramy z zewnętrznych API przez konektory
  • Logika backendowa idzie przez akcje aplikacji: wpis w actions wskazuje flow (flow_code), frontend woła POST /noe/apps/:id/action/:nazwa, a flow wykonuje resztę (konektory, transformacje, warunki). Frontend nigdy nie woła zewnętrznych API bezpośrednio
  • Bezpieczeństwo: NIGDY nie wpisuj api_token ani sekretów w source_code — kod aplikacji jest widoczny dla użytkowników. Do danych publicznych używaj public_methods bazy Noe albo konektora z metodami public: true
  • Poprawki kodu rób przez PATCH /noe/apps/:id/replace.json (field: "source_code", old_string/new_string) zamiast przesyłać cały kod od nowa

Przykład: prosta apka z custom connectorem, custom flow i cronem

Wzorzec “pobieraj cyklicznie z zewnętrznego API, przy danej wartości zareaguj, pokazuj wynik w apce”:

  1. Custom connector (kind: "noe/custom_connector") do zewnętrznego API - metody HTTP deklarujesz w fields.methods (path z interpolacją {nazwa}), bazowy adres w url, klucz API w secret_token
  2. Custom flow (kind: "noe/custom_flow") z programem w fields.program: krok connector (pobranie danych) → mutation (wyciągnięcie pól) → if z warunkiem na wyniku (np. "{json_object.temperature} > 30"), a w gałęzi then kolejny krok connector odpalający inne API (alert/powiadomienie)
  3. Cykliczna akcja konta budząca flow według harmonogramu:

    POST /account/crons.json
    { "cron": { "kind": "flow", "target_code": "KOD_FLOW", "schedule": "*/30 * * * *", "active": true } }
    

    Flow musi mieć ustawiony code. Cron przekazuje na wejściu { "kind": "cron", "cron_id": ... } - parametry startowe (np. współrzędne) ustaw pierwszym krokiem mutation. Przebiegi programu sprawdzisz w /connect/flow_processes, cron ręcznie odpalisz przyciskiem “Uruchom teraz” w /account/crons

  4. Aplikacja Noe z akcją { "action": "get_data", "flow_code": "KOD_FLOW", "public": true } - frontend woła akcję (POST /noe/apps/:id/action/get_data) i renderuje dane; cron w tle dba o cykliczne odświeżanie/alerty

Autoryzacja: Authorization: Bearer TOKEN Content-Type: application/json

API Endpoints

Metoda Sciezka Opis
GET /noe/apps.json Lista aplikacji
GET /noe/apps/:id.json Szczegoly aplikacji
POST /noe/apps.json Utworzenie aplikacji
PATCH /noe/apps/:id.json Aktualizacja
DELETE /noe/apps/:id.json Usuniecie
GET /a/:url_code Uruchomienie aplikacji (publiczny URL)
PATCH /noe/apps/:id/replace.json Czesciowa edycja pola

Pola aplikacji

Pole Typ Wymagane Opis
name string tak Nazwa aplikacji
url_code string nie Przyjazny URL slug (np. “kalkulator” -> /a/kalkulator)
description string nie Opis aplikacji
app_engine string tak Silnik: svelte, vue, react, js, html, kaboom, phaser
css_engine string tak CSS: tailwind_all (CDN), tailwind (kompilowany), none
source_code string tak Kod zrodlowy (max 200 000 znakow)
public boolean nie true = dostepna bez logowania (domyslnie false)
active boolean nie Czy aplikacja jest aktywna (domyslnie true)
db_code string nie Kod bazy Noe::Db (wiele aplikacji moze wspoldzielic jedna baze)
show_in_module string nie Osadza aplikacje w module (np. organize, mail, crm)
dashboard_widget boolean nie Apka na liscie “Dodaj kafelki” pulpitow Toolkit (zapis w fields.dashboard_widget, reszta fields zostaje). Bez tego apki nie da sie dodac jako kafelek
prompt string nie Prompt systemowy dla AI
actions array nie Definicje akcji do integracji z Flow
fields object nie Pola uzytkownika (JSON)

Silniki aplikacji

Silnik Opis Kompilacja CSS
svelte Svelte 5 z $state/$derived tak (serwer kompiluje) tak
vue Vue 3 SFC (Composition API) tak tak
react React 19 z JSX tak tak
js Czysty JavaScript nie (source_code = app_code) nie
html Czysty HTML nie (source_code = app_code) nie
kaboom Kaboom.js (gry 2D) tak nie
phaser Phaser (gry 2D/3D) tak nie

Silniki CSS

Silnik Opis Kiedy uzywac
tailwind_all Tailwind CSS przez CDN (<script src="cdn.tailwindcss.com">) Domyslny — zawsze dziala, wiekszy rozmiar strony
tailwind Tailwind kompilowany na serwerze — mniejszy rozmiar Dla produkcyjnych apek (tylko uzywane klasy)
none Bez CSS frameworka Dla apek bez stylowania lub z wlasnym CSS

Utworzenie aplikacji

POST /noe/apps.jsonContent-Type: application/json

{
  "app": {
    "name": "Nazwa aplikacji",
    "url_code": "kod-url",
    "description": "Opis",
    "app_engine": "svelte",
    "css_engine": "tailwind_all",
    "public": true,
    "source_code": "KOD_ZRODLOWY"
  }
}

Sukces — POST (201 Created)

{
  "id": "019cb0d4-47be-7ea0-b3e1-ea8bec016f89",
  "name": "Nazwa aplikacji",
  "url_code": "kod-url",
  "description": "Opis",
  "app_engine": "svelte",
  "css_engine": "tailwind_all",
  "source_code": "...",
  "public": true,
  "active": true,
  "app_build_time": 79,
  "css_build_time": 0,
  "fields": {},
  "config": {},
  "actions": [],
  "db_code": null,
  "show_in_module": null,
  "created_by_id": 1,
  "updated_by_id": null,
  "created_at": "2026-03-03T00:13:47.964+01:00",
  "updated_at": "2026-03-03T00:13:47.964+01:00"
}

Uwaga: Odpowiedz NIE zawiera: account_id, app_code, css_code, token.

Blad walidacji (422 Unprocessable Content)

{
  "name": ["can't be blank"],
  "app_engine": ["is not included in the list"],
  "source_code": ["blad kompilacji: ..."]
}

Aktualizacja aplikacji

PATCH /noe/apps/:id.jsonContent-Type: application/json

{
  "app": {
    "source_code": "NOWY_KOD"
  }
}

Mozna wyslac tylko zmienione pola — reszta pozostaje bez zmian.

Replace API (czesciowa edycja pola)

Pozwala zmienic fragment source_code, fields lub prompt bez przesylania calego pola:

PATCH /noe/apps/:id/replace.jsonContent-Type: application/json

{
  "field": "source_code",
  "old": "let count = $state(0)",
  "new": "let count = $state(10)"
}

URL aplikacji

Aplikacja jest dostepna pod adresem:

  • /a/:url_code — publiczny URL (np. /a/kalkulator)
  • /a/:id — URL po UUID (np. /a/019cb0d4-47be-7ea0-b3e1-ea8bec016f89)

Pelny URL: https://{host}/a/{url_code}

Wazne: URL to /a/ (nie /noe/).

Svelte 5 — skladnia

Svelte 5 uzywa “runes” — nowy system reaktywnosci:

<script>
  // Stan reaktywny
  let count = $state(0);
  let items = $state([]);
  let name = $state("");

  // Wartosc wyliczana (automatycznie aktualizowana)
  let doubled = $derived(count * 2);
  let total = $derived(items.length);

  // Efekt (uruchamiany przy zmianie zaleznosci)
  $effect(() => {
    console.log("Count zmieniony:", count);
  });

  // Funkcje
  function increment() {
    count++;
  }

  function addItem() {
    items = [...items, { name }];
    name = "";
  }
</script>

<!-- Template -->
<button onclick={increment}>Count: {count} (doubled: {doubled})</button>

<input bind:value={name} placeholder="Nowy element" />
<button onclick={addItem}>Dodaj</button>

{#each items as item, i}
  <div>{i + 1}. {item.name}</div>
{/each}

{#if count > 10}
  <p>Duzo!</p>
{:else}
  <p>Malo</p>
{/if}

Kluczowe roznice Svelte 5 vs Svelte 4

Svelte 4 Svelte 5
let count = 0; let count = $state(0);
$: doubled = count * 2; let doubled = $derived(count * 2);
$: { console.log(count); } $effect(() => { console.log(count); });
on:click={handler} onclick={handler}
export let prop; let { prop } = $props();

Vue 3 — skladnia

<script setup>
import { ref, computed } from 'vue';

const count = ref(0);
const doubled = computed(() => count.value * 2);

function increment() {
  count.value++;
}
</script>

<template>
  <button @click="increment">Count: {{ count }} ({{ doubled }})</button>
</template>

React 19 — skladnia

import { useState } from 'react';
import { createRoot } from 'react-dom/client';

function App() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
  );
}

createRoot(document.getElementById("noe-app")).render(<App />);

JS — skladnia

// Czysty JS — kod uruchamiany bezposrednio w przegladarce
// Kontener: document.getElementById("noe-app")
(function() {
  const container = document.getElementById("noe-app");
  container.innerHTML = '<h1>Hello World</h1>';
})();

Kontekst aplikacji (window.NoeAppContext)

Aplikacje osadzone w modulach maja dostep do danych uzytkownika:

{
  user: {
    login: "jan.kowalski",
    email: "[email protected]",
    name: "Jan Kowalski",
    role: "admin",              // admin, manager, user, guest
    team_ids: [1, 2],
    team_names: ["Sprzedaz", "IT"],
    group_ids: [5],
    group_names: ["Menedzerowie"],
    department_ids: [3],
    department_names: ["Dzial IT"]
  }
}

Osadzanie w modulach (show_in_module)

Pole show_in_module osadza aplikacje w module systemu (menu boczne, breadcrumbs).

Dostepne moduly: organize, mail, crm, kb, cms, connect, voip, form, insight, billing, drive, fiskator, commerce, account

Wstrzykiwanie na strony: show_in_module moze zawierac sciezke do strony (np. tasks/show). Wtedy apka jest wstrzykiwana bezposrednio w widok.

Wartosc Zachowanie
organize Aplikacja w menu bocznym modulu
tasks/show Wstrzyknieta na stronie szczegulow zadania
contacts/index Wstrzyknieta na liscie kontaktow

Akcje (integracja z Flow)

Akcje pozwalaja wywolywac Flow z frontendu:

{
  "app": {
    "actions": [
      {
        "action": "get_data",
        "flow_code": "my-data-flow",
        "description": "Pobiera dane z API",
        "public": true
      }
    ]
  }
}

Wywolanie z frontendu:

const response = await fetch('/noe/apps/APP_ID/action/get_data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ param1: "value1" })
});
const result = await response.json();
// result = { flow_process_id: "...", status: "pending"|"finished", data: {...} }

Polling statusu (dla dlugich operacji):

async function waitForResult(appId, actionName, flowProcessId) {
  while (true) {
    const response = await fetch(`/noe/apps/${appId}/action/${actionName}/status?flow_process_id=${flowProcessId}`);
    const result = await response.json();
    if (result.status === "finished") return result.data;
    if (result.status === "error") throw new Error(result.error);
    await new Promise(r => setTimeout(r, 1000));
  }
}

Bazy danych Noe (Noe::Db) — podsumowanie

Aplikacje moga korzystac z prostych baz danych Noe. Pełne API baz: noe_db_api.

Utworzenie bazy

POST /noe/dbs.json
{
  "db": {
    "name": "Todo",
    "code": "todo",
    "schema": {
      "fields": {
        "title": { "type": "string", "required": true },
        "done": { "type": "integer" }
      },
      "indexes": {
        "idx_str_1": "title",
        "idx_int_1": "done"
      }
    }
  }
}

Dostepne indeksy

Indeks Typ
idx_str_1, idx_str_2, idx_str_3 string (max 255 znakow)
idx_int_1, idx_int_2 bigint
idx_time_1, idx_time_2 datetime

Typy pol w schema

string, text, integer, number, boolean, datetime, date, time

Baza wielotabelowa

Jedna baza moze trzymac kilka tabel — zamiast fields na gornym poziomie podaj tables, a kazda tabela dostaje wlasne fields i wlasne indexed_fields (pelny limit indeksow per tabela):

{
  "schema": {
    "tables": {
      "users":  { "fields": { "name": { "type": "string", "required": true }, "email": "string" },
                  "indexed_fields": ["name", "email"] },
      "orders": { "fields": { "order_number": "string", "user_id": "number" },
                  "indexed_fields": ["order_number", "user_id"] }
    }
  }
}

Rekordy takiej bazy przekazuja table_name ({ "record": { "table_name": "orders", "data": {...} } }), a wyszukiwanie wymaga ?table_name=orders — bez niego endpoint zwraca 400 z lista dostepnych tabel. Frontendy appek korzystajace z metod publicznych: table_name dziala tylko w search, wiec do zapisu do konkretnej tabeli uzyj prywatnego API rekordow albo osobnej bazy jednotabelowej. Szczegoly: noe_db_api.

Operacje na rekordach (prywatne — wymaga autoryzacji)

const DB = "todo";

// Lista
const records = await fetch(`/noe/dbs/${DB}/records.json`).then(r => r.json());

// Utworzenie
await fetch(`/noe/dbs/${DB}/records.json`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ record: { data: { title: "Zadanie", done: 0 } } })
});

// Aktualizacja
await fetch(`/noe/dbs/${DB}/records/${id}.json`, {
  method: 'PATCH',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ record: { data: { done: 1 } } })
});

// Usuniecie
await fetch(`/noe/dbs/${DB}/records/${id}.json`, { method: 'DELETE' });

// Wyszukiwanie
const results = await fetch(`/noe/dbs/${DB}/search.json?q=zadanie&sort=-created_at`).then(r => r.json());

Publiczne metody (bez autoryzacji)

Pole public_methods na bazie udostepnia operacje bez logowania:

{
  "public_methods": {
    "list": { "scope_by": "user_ip" },
    "create": {
      "allowed_fields": ["title", "done"],
      "required_fields": ["title"],
      "default_values": { "done": 0 },
      "auto_fields": { "user_ip": "{ip}" }
    },
    "update": { "allowed_fields": ["title", "done"], "scope_by": "user_ip" },
    "delete": { "scope_by": "user_ip" }
  }
}

URL publiczny: POST /noe/db/:db_code/:method_name

Zmienne w auto_fields: {ip} (adres IP), {cookie_key} (unikalny token per przegladarka), {user_id} / {user_name} (zalogowany uzytkownik; puste bez sesji)

Roznica: Records API vs Search API

API Struktura danych
Records (/records.json) Zagniezdzona: record.data.field
Search (/search.json) Plaska: record.field

Kompletny przyklad — Kalkulator (Svelte 5)

POST /noe/apps.jsonContent-Type: application/json
{
  "app": {
    "name": "Kalkulator",
    "url_code": "kalkulator",
    "description": "Prosty kalkulator",
    "app_engine": "svelte",
    "css_engine": "tailwind_all",
    "public": true,
    "source_code": "<script>\n  let display = $state(\"0\");\n  let firstOperand = $state(null);\n  let operator = $state(null);\n  let waitingForSecondOperand = $state(false);\n\n  function inputDigit(digit) {\n    if (waitingForSecondOperand) {\n      display = String(digit);\n      waitingForSecondOperand = false;\n    } else {\n      display = display === \"0\" ? String(digit) : display + digit;\n    }\n  }\n\n  function inputDecimal() {\n    if (waitingForSecondOperand) { display = \"0.\"; waitingForSecondOperand = false; return; }\n    if (!display.includes(\".\")) display = display + \".\";\n  }\n\n  function clear() {\n    display = \"0\"; firstOperand = null; operator = null; waitingForSecondOperand = false;\n  }\n\n  function toggleSign() { display = String(parseFloat(display) * -1); }\n  function inputPercent() { display = String(parseFloat(display) / 100); }\n\n  function calculate(first, op, second) {\n    switch (op) {\n      case \"+\": return first + second;\n      case \"-\": return first - second;\n      case \"*\": return first * second;\n      case \"/\": return second !== 0 ? first / second : \"Error\";\n      default: return second;\n    }\n  }\n\n  function handleOperator(nextOp) {\n    const val = parseFloat(display);\n    if (operator && !waitingForSecondOperand) {\n      const result = calculate(firstOperand, operator, val);\n      display = String(result); firstOperand = result;\n    } else { firstOperand = val; }\n    waitingForSecondOperand = true; operator = nextOp;\n  }\n\n  function handleEquals() {\n    if (operator == null || waitingForSecondOperand) return;\n    const result = calculate(firstOperand, operator, parseFloat(display));\n    display = String(result); firstOperand = null; operator = null; waitingForSecondOperand = false;\n  }\n</script>\n\n<div class=\"min-h-screen bg-gray-900 flex items-center justify-center p-4\">\n  <div class=\"w-full max-w-xs bg-gray-800 rounded-2xl shadow-2xl overflow-hidden\">\n    <div class=\"p-6 pb-2\">\n      <div class=\"text-right text-white text-5xl font-light tracking-tight truncate min-h-[60px] flex items-end justify-end\">\n        {display.length > 9 ? parseFloat(display).toExponential(4) : display}\n      </div>\n    </div>\n    <div class=\"grid grid-cols-4 gap-[1px] p-3\">\n      <button onclick={clear} class=\"bg-gray-500 hover:bg-gray-400 text-black rounded-full h-16 text-2xl font-medium\">C</button>\n      <button onclick={toggleSign} class=\"bg-gray-500 hover:bg-gray-400 text-black rounded-full h-16 text-2xl font-medium\">+/-</button>\n      <button onclick={inputPercent} class=\"bg-gray-500 hover:bg-gray-400 text-black rounded-full h-16 text-2xl font-medium\">%</button>\n      <button onclick={() => handleOperator(\"/\")} class=\"bg-amber-500 hover:bg-amber-400 text-white rounded-full h-16 text-2xl font-medium\">/</button>\n      {#each [7,8,9] as d}<button onclick={() => inputDigit(d)} class=\"bg-gray-700 hover:bg-gray-600 text-white rounded-full h-16 text-2xl font-medium\">{d}</button>{/each}\n      <button onclick={() => handleOperator(\"*\")} class=\"bg-amber-500 hover:bg-amber-400 text-white rounded-full h-16 text-2xl font-medium\">x</button>\n      {#each [4,5,6] as d}<button onclick={() => inputDigit(d)} class=\"bg-gray-700 hover:bg-gray-600 text-white rounded-full h-16 text-2xl font-medium\">{d}</button>{/each}\n      <button onclick={() => handleOperator(\"-\")} class=\"bg-amber-500 hover:bg-amber-400 text-white rounded-full h-16 text-2xl font-medium\">-</button>\n      {#each [1,2,3] as d}<button onclick={() => inputDigit(d)} class=\"bg-gray-700 hover:bg-gray-600 text-white rounded-full h-16 text-2xl font-medium\">{d}</button>{/each}\n      <button onclick={() => handleOperator(\"+\")} class=\"bg-amber-500 hover:bg-amber-400 text-white rounded-full h-16 text-2xl font-medium\">+</button>\n      <button onclick={() => inputDigit(0)} class=\"bg-gray-700 hover:bg-gray-600 text-white rounded-full h-16 text-2xl font-medium col-span-2 text-left pl-6\">0</button>\n      <button onclick={inputDecimal} class=\"bg-gray-700 hover:bg-gray-600 text-white rounded-full h-16 text-2xl font-medium\">.</button>\n      <button onclick={handleEquals} class=\"bg-amber-500 hover:bg-amber-400 text-white rounded-full h-16 text-2xl font-medium\">=</button>\n    </div>\n  </div>\n</div>"
  }
}

Wynik: Aplikacja dostepna pod https://{host}/a/kalkulator

Kompletny przyklad — Todo z baza (Svelte 5)

Krok 1: Utworz baze

POST /noe/dbs.json
{
  "db": {
    "name": "Todo",
    "code": "todo",
    "schema": {
      "fields": {
        "title": { "type": "string", "required": true },
        "done": { "type": "integer" },
        "user_ip": { "type": "string" }
      },
      "indexes": { "idx_str_1": "title", "idx_str_2": "user_ip", "idx_int_1": "done" }
    },
    "public_methods": {
      "list": { "scope_by": "user_ip" },
      "create": {
        "allowed_fields": ["title", "done"],
        "required_fields": ["title"],
        "default_values": { "done": 0 },
        "auto_fields": { "user_ip": "{ip}" }
      },
      "update": { "allowed_fields": ["title", "done"], "scope_by": "user_ip" },
      "delete": { "scope_by": "user_ip" }
    }
  }
}

Krok 2: Utworz aplikacje

POST /noe/apps.json
{
  "app": {
    "name": "Todo",
    "url_code": "todo",
    "app_engine": "svelte",
    "css_engine": "tailwind_all",
    "public": true,
    "db_code": "todo",
    "source_code": "<script>\n  let tasks = $state([]);\n  let newTitle = $state(\"\");\n  const DB = \"todo\";\n\n  async function load() {\n    const res = await fetch(`/noe/db/${DB}/list`, { method: \"POST\" });\n    tasks = await res.json();\n  }\n\n  async function add() {\n    if (!newTitle.trim()) return;\n    await fetch(`/noe/db/${DB}/create`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({ data: { title: newTitle } })\n    });\n    newTitle = \"\"; await load();\n  }\n\n  async function toggle(task) {\n    await fetch(`/noe/db/${DB}/update`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({ id: task.id, data: { done: task.done ? 0 : 1 } })\n    });\n    await load();\n  }\n\n  async function remove(id) {\n    await fetch(`/noe/db/${DB}/delete`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({ id })\n    });\n    await load();\n  }\n\n  $effect(() => { load(); });\n</script>\n\n<div class=\"max-w-md mx-auto p-6\">\n  <h1 class=\"text-2xl font-bold mb-4\">Todo</h1>\n  <div class=\"flex gap-2 mb-4\">\n    <input bind:value={newTitle} placeholder=\"Nowe zadanie...\" class=\"border p-2 rounded flex-1\" onkeydown={(e) => e.key === 'Enter' && add()} />\n    <button onclick={add} class=\"bg-blue-500 text-white px-4 py-2 rounded\">Dodaj</button>\n  </div>\n  {#each tasks as task}\n    <div class=\"flex items-center gap-2 p-2 border-b\">\n      <input type=\"checkbox\" checked={task.done === 1} onchange={() => toggle(task)} />\n      <span class={task.done === 1 ? 'line-through text-gray-400' : ''}>{task.title}</span>\n      <button onclick={() => remove(task.id)} class=\"ml-auto text-red-500\">x</button>\n    </div>\n  {/each}\n</div>"
  }
}

Wskazowki

  • Znaki specjalne w JSON — przy wysylaniu przez curl unikaj em dash i innych znakow UTF-8. Uzywaj ASCII lub przesylaj z pliku (-d @plik.json)
  • Duzy source_code — przygotuj JSON w pliku i uzyj curl -d @payload.json
  • Bledy kompilacji — przy bledzie w source_code serwer zwroci 422 z opisem bledu w polu source_code
  • url_code — musi byc unikalny na koncie. Serwer automatycznie oczyci go z niedozwolonych znakow
  • Publiczna apka — ustaw "public": true aby byla dostepna bez logowania
  • Tailwind — uzywaj tailwind_all (CDN) dla prostoty lub tailwind dla mniejszego rozmiaru
  • Testowanie — po utworzeniu odwiedz https://{host}/a/{url_code} w przegladarce

Powiązane

  • noe_db_api — pełne API baz danych Noe (schema, rekordy, metody publiczne, auto_fields)
  • noe_app_example_weather — kompletny przykład krok po kroku: apka z custom connectorem, custom flow i cronem (pogoda)
  • common_api — wspólne zasady API (format, autoryzacja, odpowiedzi)

Czy ten wpis był pomocny?

Komentarze