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.
The server module exports a startServer
function that consumes
an array of Hapi routes
, a connection
configuration object, and an array of
Hapi plugins
to register, all provided by the specific web application.
import startServer from `meetup-web-platform/server';
// ...
startServer(routes, connection, plugins);
When the application server receives the JSON-encoded array of queries, it uses an API adapter module to translate those into the configuration needed to fetch data from an external API. The application server is therefore the only part of the system that needs to know how to communicate with the API.
This adapter is used to proxy all requests to /api
.
From the client-side application's point of view, it will always send
the queries
and recieve the queryResponses
for any data request -
all the API-specific translations happen on the server.
in development - API will provide most auth needs
Auth/login requires side-effect interaction with an authentication server, which has an endpoint distinct from the
regular data API - the current branch implements it as part of the API server, responding to requests to /api/login
.
on LOGIN_REQUEST
, which provides credentials
- Format
credentials
for API using aquery
for login - Make API call
- Trigger
LOGIN_SUCCESS
containing API response (could format before triggering action if needed) - Write auth cookie to maintain login across sessions
on LOGOUT
(no attached data)
- delete auth cookie (not currently implemented)
- clearing local state is not a side effect, so it's handled directly by the
auth
reducer incoreReducers
This middleware is currently only responsible for fetching the data from the API server on initial render or client-side user navigation.
on server/RENDER
or LOCATION_CHANGE
, which provide a location
(URL):
- Match
location
to definedroutes
and extract therenderProps
like URL path and querystring params - Check
routes
forquery
functions that return data needs, and process them into an array - Trigger
API_REQUEST
containing thequeries
on API_REQUEST
, which provides queries
:
- Send the queries to the application server, which will make the corresponding external API calls.
- When the application server returns data, trigger
API_SUCCESS
action containing API response array and query array - If the application server responds with an error, trigger
API_ERROR
Interesting feature: navRenderSub
is a Rx.SerialDisposable
, which means that when a user navigates to a new page,
any "pending" API requests will not be processed. This is a Very Good Thing because it means that we won't be calling
API_COMPLETE
for a page load that is no longer applicable. A similar tool is used for the AuthMiddleware so that only
one login request can be processed, but it's less likely to be an issue there since it's rare that users would be trying
to log in repeatedly without waiting for previous login requests to be processed.
The cache middleware provides optimistic state
updates for any data specified
by active route queries, similar to the SyncMiddleware, but using locally
cached data rather than an API. It is client-side only, and it does not suppress
data fetched from the server - it simply pre-empts it before the API response
returns and overwrites everything with the latest server data.
on API_REQUEST
, which provides queries
for the requested route
- For each query, use the JSON-encoded query as a key into the cache
- Format responses from the cache to look like API responses
- Trigger
CACHE_SUCCESS
containing the cache hits
on API_SUCCESS
, which provides fresh info from the server
- make an entry in the cache with
key
as the JSON-encodedquery
and thevalue
as the correspondingresponse
The cache is stored locally in the user's browser using IndexedDB so that it will survive multiple sessions. All API queries are stored, and the data is refreshed each time the query is re-requested.
In dev, be aware that the cache may be masking failed API requests - keep your network dev tools open and watch the server logs to make sure you haven't broken anything. We will add tooling to make such cases more obvious in the future.
By design, the cache masks slow responses from the API and can create a 'flash'
of stale content before the API responds with the latest data. In development,
this behavior is not always desirable so you can disable the cache by adding
a __nocache
param to the query string. The cache will remain disabled until the
the page is refreshed/reloaded without the param in the querystring.
http://localhost:8000/ny-tech/?__nocache
POST
API requests are handled by PostMiddleware
, which provides a generalized
interface for sending data to the API and handling return values. The middleware
only responds to actions that have a POST_
prefix or a _POST
suffix in the
action type
. Furthermore, the action.payload
must have a Query object (with
a type
, ref
, and params
) as well as onSuccess
and onError
actionCreators
to receive the data response from the API - they must be actionCreators because
their return values will be dispatch
ed by the middleware in the API success/error
case.
Use reducers to parse the response and update application state.