A collection of Node web application utilities developed for the Meetup web platform




This is the base platform for serving Meetup web apps including the public
website and admin. It provides a Hapi webserver and a set of conventions for
composing applications with React + Redux.
In general, application-specific code will live outside of this package.
- App configuration management
- Auth flow from requestAuthPlugin
- Analytics/tracking
- Application state management
- Rendering in consumer applications
- Caching - API and static assets
- Routing module
- Language plugin for Hapi
- API proxy plugin for Hapi
- Click and Activity tracking
- API State module
- 'Query': structuring data requests -
GET/POST/PATCH/DELETE requests to REST API
- Redux store modules - browser and server
This package uses semver versioning to tag releases, although the patch version
is determined exclusively by the Travis build number for pushes to master.
Major and minor versions are hard-coded into the Makefile.
Manual pushes to master and PR merges to master will be built by Travis, and
will kick off the yarn publish routine. The currently-published version of the
package is shown on the repo homepage on GitHub in a badge at the top of the
README.
When developing a consumer application that requires changes to the platform
code, you can release a beta version of the platform on npm by opening a PR in
the meetup-web-platform repo. When it builds successfully, a new beta version
will be added to the list of available npm versions. The generated version number
is in the Travis build logs, which you can navigate to by clicking on 'Show all
checks' in the box that says 'All checks have passed', and then getting the
'Details' of the Travis build.


At the bottom of the build log, there is a line that echos the GIT_TAG.
If you click the disclosure arrow, the version number will be displayed, e.g.0.5.177-beta.


You can then install this beta version into your consumer application with
``sh`
> yarn install meetup-web-platform@
Each time you push a change to your meetup-web-platform PR, you'll need to
re-install it with the new tag in your consumer application code.
The overall workflow is:
1. Open a PR for your meetup-web-platform branchGIT_TAG
2. Wait for Travis to successfully build your branch (this can take 5+ minutes)
3. Get the version string from the build logs under meetup-web-platform
4. (if needed) Push changes to your branch
5. Repeat steps 2-3
Basic knowledge of reactive programming using RxJS 5 is a pre-requisite for being
able to work in this repository. https://www.learnrxjs.io/ manages a good
list of starting resources, specifically:
- RxJS Introduction - Official Docs
- The Introduction to Reactive Programming You've Been Missing - André Staltz
- Asynchronous Programming: The End of The Loop - Jafar Husain
- Introduction to Reactive Programming - André Staltz
- Functional Programming in Javascript - Jafar Husain
Suggestions:
- Reference the api docs regularly while watching videos (http://reactivex.io/rxjs/).
- Play around with the JSBin in the egghead.io videos (console.log to each transformation step, etc).
The server module exports a startServer function that consumes
a mapping of locale codes to app-rendering Observables, plus any app-specific
server routes and plugins. See the code comments for usage details.
To correctly render a 'not found' state for a feature, you should render a component, which the server will use to set the response status to
404.
#### Example:
`jsx
import NotFound from 'meetup-web-platform/lib/components/NotFound';
class GroupContainer extends React.Component {
render() {
if (!this.props.group) {
return (
Sorry, no matching group was found
);
}
return
}
}
`
Activity tracking happens on every HTTP request, and is tagged with
``
platform: 'WEB',
platform_agent:
The platform also tracks clicks similar to Meetup classic.
More info in Confluence here
Use Promises or Observables to handle async processing - the latter
tends to provide more powerful async tools than the former, particularly
for long processing chains or anything involving sequences of values,
but Promises are good for single async operations. _Do not write
functions that fire callbacks_.
When using Observables, you can always throw an Error and expect theonError
subscriber to provide an handler. When using Promises, callPromise.reject(new Error( and expect that the caller will.catch()
provide a or onRejected handler.
Guidelines:
1. Use Error objects liberally - they are totally safe until they arethrow
paired with a , and even then they can be usefullytry/catch
processed without crashing the application with a .throw
2. Use when there is no clear way for the application to recover fromError
the error. Uncaught errors are allowed to crash the application and
are valuable both in dev and in integration tests.
3. Populate state with the actual object rather than just anull
Boolean or error message String. Error objects provide better
introspection data. For example, a validation process might return
(for no validation errors) or new Error('Value is required')true
rather than (for "is valid") or false.LOGIN_ERROR
4. Many errors will have an associated Redux action, such as
- keep the corresponding state updatesLOGIN_ERROR
as narrow as possible. For example, should only affectstate.app.login
- all affected UI components should read from thatLOGIN_ERROR
property rather than independently responding to in aerror
reducer. _Do not create a high-level properties state_catch
5. When using Promises or Observables, _always_ provide an error
handling function ( for Promises, error` for
Observables)