ruby on rails 3 - Server e client REST JSON API separati?




ruby-on-rails-3 backbone.js (12)

A Boundless , abbiamo approfondito l'opzione n. 2 e l'abbiamo diffusa a migliaia di studenti. Il nostro server è un'API JSON REST (Scala + MongoDB), e tutto il nostro codice client è servito direttamente da CloudFront (es .: www.boundless.com è solo un alias per CloudFront).

Professionisti:

  • Avanguardia / entusiasmante
  • Un sacco di soldi per te: l'API ti fornisce la base per il tuo client web, client mobili, accesso di terze parti, ecc.
  • Transizione pagine / caricamento pagina estremamente veloce

Contro:

  • Non SEO friendly / ready senza molto più lavoro.
  • Richiede gente di front-end di altissimo livello che sia pronta ad affrontare la realtà di un'esperienza del sito che è al 70% di javascript e che cosa significa.

Penso che questo sia il futuro di tutte le app web.

Alcuni pensieri per gli utenti del web front end (che è dove tutta la new-ness / challenge è data questa architettura):

  • CoffeeScript. Molto più facile da produrre codice di alta qualità.
  • Spina dorsale. Ottimo modo per organizzare la tua logica e la comunità attiva.
  • HAMLC. Modelli Haml + CoffeeScript => JS.
  • SASS

Abbiamo costruito un cablaggio per il nostro sviluppo front-end chiamato "Spar" (Single Page App Rocketship) che è in effetti la pipeline di asset di Rails ottimizzata per lo sviluppo di app a pagina singola. Saremo open-sourcing entro le prossime due settimane sulla nostra pagina github , insieme a un post sul blog che spiega come usarlo e l'architettura generale in modo più dettagliato.

AGGIORNARE:

Per quanto riguarda le preoccupazioni delle persone con Backbone, penso che siano sopravvalutati. La spina dorsale è molto più un principio organizzativo che una struttura profonda. Il sito Twitter stesso è una bestia gigantesca di Javascript che copre ogni caso d'angolo su milioni di utenti e browser legacy, mentre carica i tweet in tempo reale, garbage collection, visualizza molti contenuti multimediali, ecc. Di tutti i siti js "puri" che ho visto, Twitter è strano. Ci sono state molte app incredibilmente complicate distribuite tramite JS che vanno molto bene.

E la tua scelta di architettura dipende interamente dai tuoi obiettivi. Se stai cercando il modo più veloce per supportare più clienti e avere accesso a un buon talento front-end, investire in un'API standalone è un ottimo modo per andare.

Sto per creare un sacco di applicazioni web da zero. (Vedi http://50pop.com/code per panoramica.) Mi piacerebbe che fossero in grado di accedervi da molti diversi client: siti Web front-end, app per smartphone, servizi web di back-end, ecc. Quindi voglio davvero un API JSON REST per ognuno.

Inoltre, preferisco lavorare sul back-end, quindi sogno a occhi aperti di concentrarmi esclusivamente sull'API e assumere qualcun altro per realizzare l'interfaccia utente front-end, sia che si tratti di un sito Web, iPhone, Android o altra app.

Per favore aiutami a decidere quale approccio dovrei prendere:

INSIEME IN RAILS

Crea un'app web Rails molto standard. Nel controller, fai lo switch respond_with, per servire JSON o HTML. La risposta JSON è quindi la mia API.

Pro: un sacco di precedenti. Ottimi standard e molti esempi di fare le cose in questo modo.

Con: Non voglio necessariamente che l'API sia uguale all'app web. Non mi piace se / poi rispondi con l'approccio di switch. Mescolando due cose molto diverse (UI + API).

REST SERVER + JAVASCRIPT-CLIENT PESANTE

Crea un server REST API JSON-only. Utilizza Backbone o Ember.js per JavaScript lato client per accedere direttamente all'API, visualizzando i modelli nel browser.

Pro: Adoro la separazione di API e client. Le persone intelligenti dicono che questa è la strada da percorrere. Grande in teoria. Sembra all'avanguardia ed eccitante.

Con: Non molto precedente. Non molti esempi di questo hanno fatto bene. Gli esempi pubblici (twitter.com) si sentono lenti e stanno addirittura passando da questo approccio.

REST SERVER + CLIENT HTML SERVER-SIDE

Crea un server REST API JSON-only. Crea un client Web HTML di base che acceda solo all'API REST. Meno JavaScript sul lato client.

Pro: Adoro la separazione di API e client. Ma servire HTML5 semplice è abbastanza infallibile e non richiede un uso intensivo del client.

Con: Non molto precedente. Non molti esempi di questo hanno fatto bene. Anche i framework non supportano questo. Non so come affrontarlo.

Soprattutto cercando consigli dall'esperienza, non solo in teoria.


Abbiamo optato per # 2 durante la costruzione di gaug.es. Ho lavorato sull'API (ruby, sinatra, ecc.) E il mio socio in affari, Steve Smith, ha lavorato sul front-end (client javascript).

Professionisti:

  1. Muoviti rapidamente in parallelo. Se avessi lavorato prima di Steve, potrei continuare a creare API per nuove funzionalità. Se avesse lavorato prima di me, avrebbe potuto simulare molto facilmente l'API e costruire l'interfaccia utente.

  2. API gratis. L'accesso aperto ai dati nella tua app sta rapidamente diventando una funzionalità standard. Se inizi con un'API da zero, la ricevi gratis.

  3. Separazione pulita È meglio pensare alla tua app come a un'API con i clienti. Certo, il primo e più importante cliente potrebbe essere uno web, ma ti imposta per creare facilmente altri client (iPhone, Android).

Contro:

  1. Compatibilità all'indietro. Questo è più legato a un'API che alla tua domanda diretta, ma una volta che la tua API è disponibile, non puoi semplicemente romperla o romperne due. Questo non significa che devi muoverti più lentamente, ma significa che devi spesso far funzionare due cose contemporaneamente. L'aggiunta all'API o ai nuovi campi va bene, ma la modifica / rimozione non dovrebbe essere eseguita senza il controllo delle versioni.

Non riesco a pensare a più contro in questo momento.

Conclusione: il client API + JS è la soluzione migliore se si prevede di rilasciare un'API.

PS Consiglierei anche di documentare completamente la tua API prima di rilasciarla. Il processo di documentazione dell'API di Gaug.es ci ha davvero aiutato

http://get.gaug.es/documentation/api/


Attualmente sto lavorando alla conversione di un enorme CMS dall'opzione 1 all'opzione 3, e sta andando bene. Abbiamo scelto di rendere il markup lato server perché la SEO è un grosso problema per noi e vogliamo che i siti funzionino bene sui telefoni cellulari.

Sto usando node.js per il back-end del client e una manciata di moduli per aiutarmi. Sono un po 'in anticipo nel processo, ma le basi sono stabilite e si tratta di andare oltre i dati assicurando che tutto funzioni correttamente. Ecco cosa sto usando:

  • Esprimi per la base dell'app.
    (Https://github.com/visionmedia/express)
  • Richiesta di recuperare i dati.
    (Https://github.com/mikeal/request)
  • Sottolineare i template che diventano server lato rendering. Riuso questi sul client.
    (Https://github.com/documentcloud/underscore)
  • UTML avvolge i modelli underscore per farli funzionare con Express.
    (Https://github.com/mikefrey/utml)
  • Upfront raccoglie i modelli e lascia che tu scelga quali vengono inviati al client.
    (Https://github.com/mrDarcyMurphy/upfront)
  • Express Expose passa i dati recuperati, alcuni moduli e modelli al front-end.
    (Https://github.com/visionmedia/express-expose)
  • Backbone crea modelli e viste sul front-end dopo aver ingoiato i dati trasmessi.
    (Https://github.com/documentcloud/backbone)

Questo è il nucleo della pila. Alcuni altri moduli che ho trovato utili:

  • fleck (https // github.com / trek / fleck)
  • momento (https // github.com / timrwood / momento)
  • stilo (https // github.com / LearnBoost / stylus)
  • smoosh (https // github.com / fat / smoosh)
    ... anche se sto guardando in grugnito (https // github.com / cowboy / grunt)
  • traccia della console (//github.com/LearnBoost/console-trace).

No, non sto usando il coffeescript.

Questa opzione funziona davvero bene per me. I modelli sul back-end sono inesistenti perché i dati che otteniamo dall'API sono ben strutturati e lo sto passando parola per parola al front-end. L'unica eccezione è il nostro modello di layout in cui aggiungo un singolo attributo che rende il rendering più intelligente e leggero. Non ho usato alcuna libreria di modelli di fantasia per questo, solo una funzione che aggiunge quello che mi serve per l'inizializzazione e restituisce se stesso.

(scusate per i link strani, sono troppo di un n00b per l'overflow dello stack per farmi postare così tanti)


Di solito vado per la seconda opzione, usando Rails per costruire l'API e backbone per la roba JS. Puoi anche ottenere un pannello di amministrazione gratuitamente usando ActiveAdmin . Ho spedito decine di app mobili con questo tipo di back-end. Tuttavia dipende in gran parte se la tua app è interattiva o meno.

Ho fatto una presentazione su questo approccio all'ultimo RubyDay.it : http://www.slideshare.net/matteocollina/enter-the-app-era-with-ruby-on-rails-rubyday

Per la terza opzione, al fine di ottenere la reattività del 2 °, potresti provare pajax come fa Github.


Ho optato per un approccio ibrido in cui utilizziamo Sinatra come base, ActiveRecord / Postgress ecc per pubblicare i percorsi delle pagine (modelli snelli) espongono un'API REST che l'app web può utilizzare. Nelle prime fasi di sviluppo, la compilazione delle opzioni di selezione viene effettuata tramite il rendering degli helper nel modello sottile, ma quando ci avviciniamo alla produzione, questa viene sostituita per una chiamata AJAX a un'API REST mentre iniziamo a preoccuparci maggiormente della velocità di caricamento della pagina e così via.

Le cose che è facile da renderizzare in Slim vengono gestite in questo modo, e roba (popolamento di moduli, ricezione di dati POST da jQuery.Validation submitHandler ecc. È tutto a caso AJAX)

Il test è un problema. In questo momento sono stumped cercando di passare i dati JSON a un test di Rack :: Test POST .


Il server REST + client JavaScript-forte era il principio che ho seguito nel mio recente lavoro.

Il server REST è stato implementato in node.js + Express + MongoDB (ottime prestazioni di scrittura) + Mongoose ODM (ottimo per dati di modellazione, convalide incluse) + CoffeeScript (ora preferisco ES2015) che ha funzionato bene per me. Node.js potrebbe essere relativamente giovane rispetto ad altre possibili tecnologie lato server, ma mi ha reso possibile scrivere solide API con pagamenti integrati.

Ho usato Ember.js come framework JavaScript e la maggior parte della logica dell'applicazione è stata eseguita nel browser. Ho usato SASS (in particolare SCSS) per la pre-elaborazione CSS.

Ember è una struttura matura sostenuta da una forte comunità. È una struttura molto potente con un sacco di lavoro svolto di recente focalizzato sulle prestazioni, come il nuovissimo motore di rendering Glimmer (ispirato a React).

Ember Core Team è in fase di sviluppo di FastBoot , che consente di eseguire la logica di Ember JavaScript sul lato server (node.js in particolare) e inviare l'HTML pre-renderizzato dell'applicazione (che normalmente verrebbe eseguito nel browser) all'utente. È ottimo per la SEO e l'esperienza utente, dal momento che non aspetta così tanto che la pagina venga visualizzata.

Ember CLI è un ottimo strumento che ti aiuta ad organizzare il tuo codice e ha fatto bene a ridimensionare con il codebase in crescita. Ember ha anche il proprio ecosistema di addon e puoi scegliere tra una varietà di Addons Ember . Puoi facilmente prendere Bootstrap (nel mio caso) o Foundation e aggiungerlo alla tua app.

Non per servire tutto tramite Express, ho scelto di usare nginx per servire immagini e client pesanti di JavaScript. L'utilizzo del proxy nginx è stato utile nel mio caso:

upstream app_appName.com {
  # replace 0.0.0.0 with your IP address and 1000 with your port of node HTTP server
  server 0.0.0.0:1000;
  keepalive 8;
}

server {
  listen 80 default_server;
  listen [::]:80 default_server ipv6only=on;

  client_max_body_size 32M;

  access_log  /var/log/nginx/appName.access.log;
  error_log  /var/log/nginx/appName.error.log;

  server_name appName.com appName;

  location / {
     # frontend assets path
     root /var/www/html;
     index index.html;

     # to handle Ember routing
     try_files $uri $uri/ /index.html?/$request_uri;
  }

  location /i/ {
    alias /var/i/img/;
  }

  location /api/v1/ {
    proxy_pass  http://app_appName.com;

    proxy_next_upstream error timeout invalid_header http_500 http_502
http_503 http_504;
    proxy_redirect off;
    proxy_buffering off;
    proxy_set_header        Host            $host;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

Pro: Adoro la separazione di API e client. Le persone intelligenti dicono che questa è la strada da percorrere. Grande in teoria. Sembra all'avanguardia ed eccitante.

Posso dire che è anche molto utile nella pratica. Un altro vantaggio della separazione dell'API REST è che è possibile riutilizzarlo in un secondo momento per altre applicazioni. Nel mondo perfetto dovresti essere in grado di utilizzare la stessa API REST non solo per la pagina web, ma anche per le applicazioni mobili se decidessi di scriverne una.

Con: Non molto precedente. Non molti esempi di questo hanno fatto bene. Gli esempi pubblici (twitter.com) si sentono lenti e stanno addirittura passando da questo approccio.

Le cose sembrano diverse ora. Ci sono molti esempi di fare API REST + molti clienti che lo consumano.


Mi piace # 3 quando il mio sito web non sarà un'implementazione CRUD al 100% dei miei dati. Che deve ancora accadere.

Preferisco sinatra e dividerò l'app in poche app per rack con scopi diversi. Creerò un'app per rack specifica per API che coprirà ciò di cui ho bisogno per l'API. Allora forse un'app per rack utente che presenterà la mia pagina web. A volte quella versione interrogherà l'API se necessario, ma di solito si preoccupa solo del sito html.

Non mi preoccupo e faccio una query sul livello di persistenza dal lato utente se ne ho bisogno. Non sono eccessivamente preoccupato di creare una separazione completa poiché di solito finiscono per servire a scopi diversi.

Ecco un esempio molto semplice di utilizzo di più app per rack. Ho aggiunto un rapido esempio di jQuery affinché tu possa vederlo colpire l'app API. Puoi vedere quanto può essere semplice con sinatra e montare più app per rack con scopi diversi.

https://github.com/dusty/multi-rack-app-app


Molto bene chiesto. +1. Di sicuro, questo è un riferimento utile futuro per me. Anche @Aaron e altri hanno aggiunto valore alla discussione. Come Ruby, questa domanda è applicabile anche ad altri ambienti di programmazione.

Ho usato le prime due opzioni. Il primo per numerose applicazioni e il secondo per il mio progetto open source Cowoop

opzione 1

Questo è senza dubbio il più popolare. Ma trovo che l'implementazione sia molto http-ish. Il codice iniziale di ogni API riguarda l'oggetto richiesta. Quindi il codice API è più del puro codice ruby ​​/ python / altro linguaggio.

opzione 2

L'ho sempre amato

Questa opzione implica anche che HTML non è generato dal runtime sul server. Ecco come l'opzione 2 è diversa dall'opzione 3. Ma sono costruiti come html statico usando uno script di compilazione. Quando caricati sul lato client questi HTML chiamerebbero server API come client API JS.

  • La separazione delle preoccupazioni è un grande vantaggio. E gli esperti di backend che ti piacciono (e quelli miei) implementano le API di backend, li testano facilmente come un normale codice lingua senza preoccuparsi del codice di richiesta framework / http.

  • Questo non è così difficile come sembra dal lato frontale. Le chiamate API e i dati risultanti (principalmente json) sono disponibili per il modello lato client o MVC.

  • Minore elaborazione lato server. Significa che puoi andare per hardware di base / server meno costoso.

  • Più facile per testare i livelli in modo indipendente, più facile da generare documenti API.

Ha alcuni aspetti negativi.

  • Molti sviluppatori lo trovano troppo ingegnerizzato e difficile da capire. Quindi è probabile che l'architettura venga criticata.

  • i18n / l10n è difficile. Poiché HTML è generato essenzialmente, i tempi di compilazione sono statici, occorrono più build per lingua supportata (che non è necessariamente una cosa negativa). Ma anche con questo potresti avere casi d'angolo intorno a l10n / i18n e devi stare attento.

Opzione 3

La codifica del backend in questo caso deve essere uguale alla seconda opzione. La maggior parte dei punti per l'opzione 2 sono applicabili anche qui.

Le pagine Web vengono renderizzate in runtime utilizzando i modelli lato server. Ciò rende i18n / l10n molto più semplice con tecniche più consolidate / accettate. Potrebbe essere una chiamata http in meno per un contesto essenziale necessario per il rendering della pagina come utente, lingua, valuta, ecc. Quindi l'elaborazione lato server è aumentata con il rendering ma probabilmente compensata da meno chiamate http al server API.

Ora che le pagine sono rese server sul server, il frontend è ora più legato all'ambiente di programmazione. Questo potrebbe non essere nemmeno una considerazione per molte applicazioni.

Caso Twitter

Come ho capito, Twitter potrebbe eseguire il rendering della pagina iniziale sul server, ma per gli aggiornamenti della pagina ha ancora alcune chiamate API e modelli lato client per manipolare DOM. Quindi in tal caso hai dei doppi template da mantenere che aggiungono un sovraccarico e una complessità. Non tutti possono permettersi questa opzione, a differenza di Twitter.

Il nostro progetto Stack

Mi capita di usare Python. Io uso JsonRPC 2.0 invece di REST. Suggerisco REST, anche se mi piace l'idea di JsonRPC per vari motivi. Io uso sotto le biblioteche. Qualcuno considerando l'opzione 2/3 potrebbe trovarlo utile.

  • Server API: Python Un micro framework web veloce - Flask
  • Server frontend: Nginx
  • MVC lato client: Knockout.js
  • Altri strumenti / librerie rilevanti:

La mia conclusione e la mia raccomandazione

Opzione 3!

Detto ciò, ho usato con successo l'opzione 2, ma ora mi sono orientata verso l'opzione 3 per un po 'di semplicità. La generazione di pagine HTML statiche con script di build e il loro servizio con uno dei server ultraveloci specializzati nel servire pagine statiche è molto allettante (opzione 2).


Personalmente preferisco l'opzione (3) come soluzione. È usato in quasi tutti i siti che un ex datore di lavoro (di famiglia) ha. Significa che puoi ottenere alcuni sviluppatori front-end che sanno tutto su Javascript, stranezze del browser e quant'altro per codificare il tuo front-end. Devono solo sapere "arricciare xyz e avrai un po 'di soldi" e se ne andranno.

Nel frattempo, i tuoi tipi pesanti di back-end possono programmare i provider Json. Questi ragazzi non hanno bisogno di pensare alla presentazione del tutto, e invece si preoccupano di backsw flaky, timeout, gestione degli errori aggrovigliata, pool di connessione al database, threading e ridimensionamento ecc.

L'opzione 3 offre una buona e solida architettura a tre livelli. Significa che le cose che sputi dal front-end sono SEO friendly, possono essere fatte funzionare con vecchi o nuovi browser (e quelli con JS disattivato), e potrebbero ancora essere Javascript templating sul lato client se vuoi (così potresti fare cose come gestire vecchi browser / googlebot con HTML statico, ma inviare JS esperienze dinamiche per persone che utilizzano l'ultimo browser Chrome o qualsiasi altra cosa).

In tutti i casi ho visto l'Opzione 3, è stata una implementazione personalizzata di alcuni PHP che non è particolarmente trasferibile tra i progetti, per non parlare di Open Source. Immagino che più recentemente PHP possa essere stato sostituito da Ruby / Rails, ma lo stesso genere di cose è ancora vero.

FWIW, $ current_employer potrebbe fare con l'Opzione 3 in un paio di posti importanti. Sto cercando una buona struttura di Ruby in cui costruire qualcosa. Sono sicuro di poter incollare un carico di gemme, ma preferirei un singolo prodotto che fornisca in generale un modello, "curling", autenticazione facoltativa, soluzione di memorizzazione nella cache connessa di memcache / nosql opzionale. Lì non sto trovando nulla di coerente :-(


Preferisco seguire il percorso n. 2 e n. 3. Principalmente perché il # 1 viola la separazione delle preoccupazioni e mescola tutti i tipi di cose. Alla fine troverai la necessità di avere un endpoint dell'API che non ha una pagina HTML corrispondente / etc e sarai su un torrente con endpoint HTML e JSON mescolati nella stessa base di codice. Si trasforma in un pasticcio, anche se è MVP, dovrai riscriverlo alla fine perché è così caotico che non vale nemmeno la pena salvarlo.

Andando con # 2 o # 3 è possibile avere completamente un'API che agisce allo stesso modo (per la maggior parte) a prescindere. Ciò fornisce una grande flessibilità. Non sono ancora venduto al 100% su Backbone / ember / Whatever / etcjs. Penso che sia fantastico, ma come stiamo vedendo con Twitter questo non è ottimale. MA ... Twitter è anche una grande bestia di un'azienda e ha centinaia di milioni di utenti. Quindi qualsiasi miglioramento può avere un impatto enorme sul risultato economico in varie aree delle varie unità aziendali. Penso che ci sia molto di più nella decisione che nella velocità e non ci lasciano entrare. Ma questa è solo la mia opinione. Tuttavia, non sconto la spina dorsale e i suoi concorrenti. Queste app sono fantastiche da usare e sono molto pulite e sono molto reattive (per la maggior parte).

Anche la terza opzione ha qualche alleanza valida. È qui che seguirò il principio di Pareto (regola 80/20) e avrò il 20% del tuo markup principale (o viceversa) renderizzato sul server e poi avrò un buon client JS (backbone / etc) eseguirà il resto di esso . Potresti non comunicare al 100% con l'API REST tramite il client JS, ma dovrai fare del lavoro se necessario per migliorare la tua esperienza.

Penso che questo sia uno di quei tipi di problemi che "dipende" e la risposta è "dipende" da ciò che stai facendo, da chi stai servendo e dal tipo di esperienza che desideri che essi ricevano. Dato che penso che tu possa decidere tra 2 o 3 o un ibrido di loro.


Una domanda molto bella e sono sorpreso perché ho pensato che questo è un compito molto comune al giorno d'oggi in modo tale che avrò un sacco di risorse per questo problema, tuttavia si è rivelato non essere vero.

I miei pensieri sono i seguenti: - Crea qualche modulo che abbia la logica comune tra i controller API e i controller HTML senza restituire json o rendering html e includi questo modulo sia nel controller HTML che nel controller API, quindi fai quello che vuoi, quindi per esempio :

module WebAndAPICommon
    module Products

        def index
            @products = # do some logic here that will set @products variable
        end

    end
end


class ProductsController < ApplicationController
    # default products controlelr, for rendering HMTL pages 
    include WebAndAPICommon

    def index
        super
    end

end



module API
    class ProductsController
        include WebAndAPICommon

        def index
            super
            render json: @products
        end

    end
end

Utilizziamo la seguente variante di # 3: Crea un server REST API JSON-only. Crea un server Web HTML. Il server Web HTML non è, come nella tua variante, un client per il server API REST. Invece, i due sono pari. Non molto al di sotto della superficie, c'è un'API interna che fornisce la funzionalità di cui hanno bisogno i due server.

Non siamo a conoscenza di precedenti, quindi è un po 'sperimentale. Finora (in procinto di entrare in beta), ha funzionato abbastanza bene.





ember.js