API page e API custom
Una API page è un oggetto page con PageType = API: non viene disegnata per il client, ma serializzata in JSON su un endpoint REST. La sua identità nell’URL nasce da APIPublisher, APIGroup e APIVersion, più EntityName (singolare) ed EntitySetName (plurale): è questo che permette a due estensioni di esporre entità omonime senza collidere. Le API standard di Microsoft vivono sotto /api/v2.0/, quelle custom sotto il percorso costruito con publisher, group e versione. La chiave esposta come id è il SystemId, un GUID stabile, non il numero documento visibile a video.
Il punto che l’esame vuole vedere chiaro è il confronto con il web service. Una API page è pubblicata dall’estensione stessa all’installazione, non richiede setup manuale, nasce per l’integrazione (payload compatto, DelayedInsert, supporto a $filter, $select, $expand, notifiche webhook) ed è versionata. Un page o un codeunit esposto come web service va invece registrato a runtime nella lista dei web service del tenant, resta modificabile dall’amministratore e porta con sé la logica pensata per l’interfaccia utente. Contratto stabile destinato a terzi: API page custom. Apertura rapida di qualcosa che già esiste: web service. Sulle API standard la regola pratica è non alterarne il contratto, ma affiancare una propria API page.
Azioni OData: bound e unbound
Un endpoint di sole entità copre il CRUD, non i processi. Per innescare logica di business servono le azioni OData, e la distinzione fra le due forme è quella che l’esame verifica davvero.
Un’azione bound è legata a una risorsa: è una procedura marcata [ServiceEnabled] dichiarata dentro la API page, e agisce sull’istanza identificata dalla chiave presente nell’URL. È la forma corretta quando l’operazione non ha senso senza il record — registrare un documento, rilasciare un ordine, inviare una fattura.
Un’azione unbound non ha un record padre: è una procedura [ServiceEnabled] dentro un codeunit pubblicato come web service, invocata sull’endpoint OData V4 del servizio senza chiave di risorsa. È la scelta per funzioni globali: avviare un’elaborazione, restituire un calcolo, lanciare un job.
Query, web service e Read Scale-Out
Il query object mette in join e aggrega dati in sola lettura; con QueryType = API diventa esso stesso un endpoint, altrimenti si pubblica come web service OData. È la scelta giusta per letture massive e analisi esterne, dove una page trascinerebbe trigger e logica di presentazione. Riepilogando i canali: SOAP espone page e codeunit ed esiste per l’interoperabilità legacy; OData V4 espone page e query, e per le procedure [ServiceEnabled] di un codeunit anche le unbound action; le API page stanno sopra, come contratto versionato.
Read Scale-Out indirizza le sole letture verso una replica secondaria del database, alleggerendo l’istanza che serve il carico transazionale. Si governa con la proprietà DataAccessIntent (ReadOnly o ReadWrite) su report, query, page e codeunit, e l’amministratore può sovrascrivere l’intent dalla pagina di gestione senza toccare il codice. Conviene su report pesanti, query analitiche, esportazioni e job queue di sola lettura; non va usato su oggetti che scrivono, e va messo in conto un minimo ritardo di allineamento della replica.
Verso Power Platform
Le stesse API sono la base dell’integrazione con Power Platform: il connettore Business Central per Power Automate e Power Apps consuma gli endpoint, mentre le virtual table di Dataverse proiettano i dati senza duplicarli, lasciando Business Central come sistema di record. In ambiente online il tenant applica throttling: un client corretto gestisce le risposte HTTP 429 con retry e backoff.
Trappole tipiche d’esame
- Operazione che agisce su un record specifico → azione OData bound: procedura
[ServiceEnabled]nella API page; se lo scenario cita l’id della risorsa nell’URL, la risposta unbound è distrattore. - Funzione globale senza record di riferimento → azione OData unbound: procedura
[ServiceEnabled]in un codeunit pubblicato come web service; metterla in una API page la renderebbe bound. - Report pesante che rallenta gli utenti →
DataAccessIntent = ReadOnly: trappola frequente, l’oggetto scrive anche solo una riga di log e in sessione ReadOnly fallisce a runtime. - Contratto stabile per un partner esterno → API page custom versionata: pubblicare la page dell’interfaccia come web service espone un contratto che cambia a ogni modifica UI.
- Lettura aggregata da più tabelle → query object: una API page su una singola tabella con codice di aggregazione nei trigger è la risposta sbagliata per volumi e performance.
- Integrazione con Power Apps o Dataverse → connettore e virtual table sopra le API: l’accesso diretto al database non è un’opzione in SaaS.