All Projects → bfirsh → Otter

bfirsh / Otter

Licence: other
A server that runs your client-side apps.

Programming Languages

javascript
184084 projects - #8 most used programming language
coffeescript
4710 projects

Projects that are alternatives of or similar to Otter

Universal Router
A simple middleware-style router for isomorphic JavaScript web apps
Stars: ✭ 1,598 (+1897.5%)
Mutual labels:  server-side-rendering, single-page-app
stimulus todomvc
[WIP] An implementation of TodoMVC using Ruby on Rails and StimulusJS
Stars: ✭ 14 (-82.5%)
Mutual labels:  single-page-app, server-side-rendering
React Imvc
An Isomorphic MVC Framework
Stars: ✭ 211 (+163.75%)
Mutual labels:  server-side-rendering, single-page-app
Easywebpack
A Simple, Powerful Webpack Front-End Development Solution
Stars: ✭ 452 (+465%)
Mutual labels:  server-side-rendering, single-page-app
FlexDotnetCMS
A powerful, flexible, decoupled and easy to use and Fully Featured ASP .NET CMS, it can also be used as a Headless CMS
Stars: ✭ 45 (-43.75%)
Mutual labels:  single-page-app, server-side-rendering
React App
Create React App with server-side code support
Stars: ✭ 614 (+667.5%)
Mutual labels:  server-side-rendering, single-page-app
React Cool Starter
😎 🐣 A starter boilerplate for a universal web app with the best development experience and a focus on performance and best practices.
Stars: ✭ 1,083 (+1253.75%)
Mutual labels:  server-side-rendering
Jsonapi React
A minimal JSON:API client and React hooks for fetching, updating, and caching remote data.
Stars: ✭ 65 (-18.75%)
Mutual labels:  server-side-rendering
Next Pkg
Extended Next.js server with pkg support
Stars: ✭ 55 (-31.25%)
Mutual labels:  server-side-rendering
Vynchronize
Watch videos with friends online with the new real time video synchronization platform
Stars: ✭ 1,072 (+1240%)
Mutual labels:  single-page-app
Laravel Vue Starter
Well Documented Laravel Starter App From Development to Production. For Full Blown RESTFUL API and SPA with Beautiful UI Using Buefy / ElementUi For Reusable Vue Components
Stars: ✭ 76 (-5%)
Mutual labels:  single-page-app
Vue Mix
Bootstrap your Vue 2.0 SPA in less than 2 minutes.
Stars: ✭ 69 (-13.75%)
Mutual labels:  single-page-app
Rpg Boilerplate
Relay (React), Postgres, and Graphile (GraphQL): A Modern Frontend and API Boilerplate
Stars: ✭ 62 (-22.5%)
Mutual labels:  server-side-rendering
Cloudflare Worker Preact Pwa
Cloudflare worker running a Preact Progressive Web App
Stars: ✭ 57 (-28.75%)
Mutual labels:  server-side-rendering
Nativescript Ionic Template
📱 🖥 Create Mobile First apps, Web and Native sharing the code with Angular 🎉
Stars: ✭ 65 (-18.75%)
Mutual labels:  single-page-app
Scalajs Bootstrap
Scala.js bootstrap components
Stars: ✭ 55 (-31.25%)
Mutual labels:  single-page-app
Rubel
Rubel is a cms built with Laravel and React.
Stars: ✭ 70 (-12.5%)
Mutual labels:  single-page-app
Ssr Window
Better handling for window object in SSR environment
Stars: ✭ 55 (-31.25%)
Mutual labels:  server-side-rendering
Ng Toolkit
⭐️ Angular tool-box! Start your PWA in two steps! Add Serverless support for existing projects and much more
Stars: ✭ 1,116 (+1295%)
Mutual labels:  server-side-rendering
Ssr React
How to server-side render React, hydrate it on the client and combine client and server routes
Stars: ✭ 67 (-16.25%)
Mutual labels:  server-side-rendering

Otter

A server that runs your client-side apps.

Wait, what?

Web apps are shifting client-side. More and more logic is moving from server to client, but that often ends up with your server just serving up a JSON API and a blank index.html which gets filled with content client-side. This sucks for two reasons:

  • It's slow. You must make at least two round trips to the server before any content is displayed.
  • Your content is invisible to search engines, curl, browsers with JavaScript disabled, etc.

A typical solution to this problem is to write server-side code that renders some of what the client would render. This works, but now you're writing everything twice.

But what if we could use client-side APIs on the server? What if we could generate HTML with the DOM and jQuery? Make HTTP requests with XMLHTTPRequest? This would mean you could run your client-side code on the server without modification.

Otter does just that. When a client makes a request to Otter, it loads up your app inside Zombie.js, a Node implementation of the browser APIs. After it has finished loading a page, it renders the DOM to a string and sends it back to the client.

If the client supports JavaScript, they can start up the client-side router, and it's business as usual. If the client can't run JavaScript, they just see the content as a normal web page.

You've now got an application which behaves like it was written in a server-side language, but actually shares its code with the client.

So I'll have to write my server in JavaScript?

Nope! You won't have to modify your server.

Otter is a stand-alone server which runs client-side JavaScript. The app running inside Otter talks to the same server that your browser does. If you've got a Backbone app, it talks to the same server which serves JSON for your models.

So I'll have to write my client-side code with Backbone?

Nope! Unlike other techniques that allow running the same code on the server and in the browser, Otter is framework agnostic. It's an implementation of the browser APIs on the server, so almost any code which runs inside the browser will run inside Otter.

Is it secure?

Otter is far more paranoid than a browser, so you won't trip up on common client-side vulnerabilities.

All code runs inside a sandbox. Node's sandboxes aren't perfect though – you must still ensure that you always run trusted code – but to help with that, Otter only allows HTTP requests to the local server by default. If you wish to load data from other domains, you must allow them explicitly.

Install

$ sudo npm install -g otter

(Or use your preferred way of installing npm packages.)

Getting started

Otter is, at a basic level, an HTTP server. Pointed at a directory, it will serve the files inside it. It only starts doing clever things when it is asked to serve a file that doesn't exist.

Instead of showing a 404, it will open the file index.html in Zombie.js. When Zombie.js finishes loading the page (all Ajax requests have finished, etc) it sends document.outerHTML as the HTTP response back to the browser.

To demonstrate how Otter works, an example app is included in example/. It's a simple Twitter client written in Backbone. The first page load runs server-side, then the client instantiates Backbone's router to handle subsequent requests. It uses backbone-otter to handle caching between server and client.

Run Otter on that directory, allowing requests to api.twitter.com:

$ otter -a api.twitter.com example/
Server started on port 8000.

Point your browser at http://localhost:8000.

Usage

$ otter [options] <path>

Otter is passed a path to a directory which is expected to contain an index.html file. It takes these options:

-a host1,host2

A comma-separated list of hosts to allow connections to (e.g. api.example.com,api.twitter.com). By default, Otter will not allow connections to any host except itself. If you want to allow Ajax connections to your API, for example, you will need to add it to this list.

-p port

The port to listen on. Default: 8000

-w num

The number of worker processes to spawn, defaulting to the number of CPUs.

API

Otter provides an API to use inside your apps, exposed as window.otter on both the server and the client.

window.otter.isServer

true or false, whether or not the page is running on the server or client.

window.otter.cache

An object that can be used to pass data from the server to the client.

When your app is running inside Otter, it can set keys on this object. The object is serialised to JSON and injected into the top of the page sent to the browser. When the browser opens the page, the value of window.otter.cache is restored from the serialised JSON.

See the section Resuming your app on the client for an example of how this object can be used.

Writing an app for Otter

Writing an app for Otter is almost the same as writing a single-page app just for the browser, but there are a few things you need to take into account.

Running code only on the server or the client

Some code only makes sense to run on the client; for example, handling user interactions, drawing to canvases etc. You can use the window.otter.isServer variable to check if you are running on the server:

if (window.otter && window.otter.isServer) {
    // Running on the server
}
else {
    // Running in the browser
}

Resuming your app on the client

Reinstantiating the app on the client after the app has been run on the server, in order that it can handle user interaction and route future pages, is a tricky problem. Otter is framework agnostic, so it doesn't prescribe a solution. It does, however, provide tools, such as window.otter.cache (see API) and backbone-otter if you're using Backbone.

The brute-force approach is to reroute the URL, completely rebuilding the page client-side. This isn't as scary as it sounds if you cache the data that was fetched on the server, but the downsides of this are obvious inefficiency, and possibly odd side-effects of loading in a new copy of the DOM, if the user has already interacted with the initial DOM.

If you want a more efficient solution, we can work smarter. We can cache data that Otter fetches from your API, and pass it on to the client. If we then rebuild a set of models and views attached to the correct DOM elements that the server has generated, we can "boot up" the application again without having to regenerate the HTML. In Backbone, this is a matter of only rendering a view if it hasn't already been rendered by the server. See the included example app for a simple demonstration of how this can be done.

I am working on some Backbone tools for Otter to make this process easier.

Redirects

Otter will intercept changes in location (e.g. setting window.location) and immediately respond with a 302 redirect. This causes the browser to change location as you would expect if the code was running client-side.

Cookies

Cookies in an HTTP request to Otter will be passed through to document.cookie so that they are available inside Otter. Similarly, any cookies set in document.cookie will be sent back with the HTTP response to the browser.

Deployment

Otter apps can be deployed to Heroku. Within your app, you'll want to create a package.json file that specifies Otter as a dependency:

    {
      "name": "otter-example",
      "version": "0.1.0",
      "dependencies" : {
        "otter": "*"
      }
    }

You'll also need a Procfile to tell Heroku how to run Otter, assuming index.html and the rest of your app is in a directory called app/:

    web: ./node_modules/.bin/otter -p $PORT app/

Then deploy to Heroku as usual.

Take a look at Hao-kang Den's kawauso for a more complete example.

Extending Otter

Instead of running Otter standalone, it can also be extended by using it as part of a Node.js app. See lib/otter/server.coffee for the Express app that is used internally. The renderer used in this file is available externally as require('otter').renderer.

Running the test suite

$ npm test
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].