REST Api Design Patterns

3

Sto sviluppando un Api REST usando Node.js & Express.js . Recentemente ho iniziato a cercare le migliori pratiche per il design REST Api, ma è un po 'confuso quindi ti prego di sopportare me.

Ad esempio:

Diciamo che ho le seguenti raccolte di API:

  1. Utenti
  2. Attività (ogni attività deve appartenere a un utente)

E avere queste risorse:

  • OTTIENI /api/users restituisce tutti gli utenti
  • OTTIENI /api/users/:id restituisce un utente specifico
  • POST /api/users creerà un singolo utente
  • POST /api/users/:id aggiornerà un utente specifico
  • POST /api/tasks creerà una nuova attività ma la assegnerà all'utente connesso.
  • POST /api/tasks/:id aggiornerà una specifica attività
  • OTTIENI /api/tasks otterrà tutte le attività
  • OTTIENI /api/users/:id/tasks otterrà tutte le attività per un utente specifico
  • OTTIENI /api/users/:id/tasks/:id otterrà un'attività specifica per un utente specifico

Ora ogni "risorsa" sopra è reindirizzato verso un "controllore " che esegue effettivamente azioni.

Ad esempio:

POST ('/api/users', myController)

let myController = (req,res) => {
    Create new User
    Save to Database
    Call Private Send an Email to Admin function etc
 }

Domande:

Diciamo che devo eseguire alcune query su una risorsa come filter dovrei:

OPZIONE 1:

Crea una nuova risorsa come /api/users/:id/tasks/filter e all'interno del mio controller ed esegui le azioni di filtro sul database all'interno del controller

OPZIONE 2:

Utilizza stringhe di query come /api/users/:id/tasks?filter=title::someValue&priority::someOtherValue . In base alle mie conoscenze, NON crea una nuova risorsa, ma chiameremo semplicemente la risorsa /api/users/:id/tasks con le stringhe di query sopra.

Se seguo OPZIONE 2 questo significa che avrò un solo controller che gestisce le azioni /api/users/:id/tasks in modo da gestire qualsiasi query per questa risorsa dovrei fare quanto segue:

GET ('/api/users/:id/tasks', myController)

let myController = (req,res) => {

    if(req.filter)
    {handle Query here and get data from database and return to user}

    else if(some other query)
    {handle query here}

    else if(some other query)
    {handle query here}

    else {
      if there are NO queries then get get all Tasks for a specific User.
    }

 }

L'implementazione / concetto di cui sopra è corretta? A me sembra disordinato e confuso. Quindi cosa sto facendo di sbagliato qui? Qual è il modo corretto di gestire le query? Un controller singolo per una risorsa dovrebbe gestire tutto per quella particolare risorsa?

Ho problemi a pensare in "REST" ci sono così tante informazioni che non sono sicuro di cosa sia giusto e che cosa non va. Quindi i miei principi di base sono un po 'deboli quindi qualsiasi guida a tale proposito sarebbe molto utile (eventuali articoli o risorse online).

    
posta Lorenzo von Matterhorn 23.11.2017 - 11:50
fonte

2 risposte

1

responsabilità

Nonostante la domanda sia stata definita come REST, la verità è che non ha nulla a che fare con REST.

Per farla breve, come mappare i parametri della richiesta in un linguaggio di query specifico sono i dettagli di implementazione . Quindi, per ora, lasciamo da parte REST.

Is the above implementation/concept correct?

Per noi per dire se è corretto o meno, avremmo bisogno di conoscere il progetto e le sue esigenze. Finora, quello che possiamo dire è: la soluzione non è flessibile .

Flessibile nel senso di ridimensionamento a lungo termine . Ogni nuovo filter ti richiederebbe (almeno) di modificare controller , il livello di servizio e forse anche il livello di accesso ai dati. Dal punto di vista gestionale, è difficile comprare così tanto lavoro per una così piccola funzionalità. In altre parole, la manutenzione sembra costosa.

Tuttavia, dipende dalla frequenza con cui si aggiungono nuovi parametri alla query.

So what am I doing wrong here?

Mancano due possibili astrazioni.

  1. Quello che associa un insieme dinamico di parametri di richiesta a un modello specifico dell'API.
  2. Quello che associa il modello precedente alla tua specifica lingua di query .

Nota : alcuni framework rendono # 1 opzionale.

In questo momento la mappatura è hardcoded come blocco if/else . Troppo rigido per ridimensionare bene.

Invece, potrebbe essere simile a:

myController(req,res){

   var query = new QueryParametersMapper(req);

   var page = new PageParametersMapper(req);

   var sort = new SortParametersMapper(req);

   var dataSet = myRepository.find(query, page, sort);

  // etc...
}

What's the correct way of handling queries?

Rendi la mappatura più dinamica e riutilizzabile .

A questo punto, il mio primo consiglio sarebbe cercare librerie compatibile con il tuo framework o linguaggio di programmazione.

Il mio secondo consiglio sarebbe, scegliere quello che supporta Convenzione sulla configurazione .

Anche se non ti impedisce di apportare modifiche, almeno (ne sono sicuro) i costi delle modifiche saranno inferiori a quelli attuali. *

* Se consideriamo il codice fornito come esempio nella domanda.

    
risposta data 16.02.2018 - 21:11
fonte
0

Ecco le mie opinioni come sviluppatore API RESTful. Alcuni di questi non hanno nulla a che fare con REST ma solo una buona pratica HTTP.

  • Accetta l'opzione n. 2 e utilizza le stringhe di query. Questo sarà migliore per gli intermediari come la cache del browser.
  • Invece di ?filter=title::someValue&priority::someOtherValue consiglierei ?title=someValue&priority=someOtherValue
  • Per utilizzare ?filter=title::someValue&priority::someOtherValue nel modo in cui avevi inteso, dovresti comunque codificare tramite URL la e commerciale.
  • Consiglio vivamente di non mettere /api/ nei tuoi URL. Non vi è alcuna differenza tra un endpoint dell'API rivolto agli esseri umani e uno rivolto a macchine diverse dalla rappresentazione, che è indipendente dall'URL. La condivisione di un URL consente agli stessi link di essere seguiti da uomini o macchine e per ciascuno di essi di ottenere la rappresentazione appropriata per loro. Se /users/:id restituisce una pagina HTML e /api/user/:id restituisce JSON, stai "sbagliando", perché entrambi gli URL rappresentano la stessa cosa (tecnicamente, non è sbagliato: più URL hanno il pieno diritto di identificare la stessa risorsa -Ma potrebbe essere migliore)
  • Allo stesso modo, non utilizzerei /api/users/:id/tasks/:id per le singole attività e ho una raccolta /tasks e /tasks/:id al di sotto di questo, con i link da /users/:id/tasks diretti direttamente a quest'ultimo. / attività e tutto ciò che è sotto sarebbe controllato dall'accesso proprio come / utenti.

Dici anche:

According my knowledge doing this will NOT create a new resource

Lo farà assolutamente! Ogni URL univoco è una risorsa diversa. Non importa se restituisce lo stesso contenuto di un altro URL, o reindirizza a un altro URL, oppure ha un <link rel=canonical> in qualche altro URL, oppure è un sottoinsieme di un altro URL. Un URL identifica un concetto astratto di una "risorsa", alcuni dei quali possono essere rappresentati come un flusso di byte e altri no.

    
risposta data 18.12.2017 - 16:32
fonte

Leggi altre domande sui tag