Skip to content

Latest commit

 

History

History
294 lines (238 loc) · 13.1 KB

frontend.md

File metadata and controls

294 lines (238 loc) · 13.1 KB
id title description
frontend
Frontend
This document describes techniques that you can use to implement a frontend for a reSolve application.

This document describes techniques that you can use to implement a frontend for a reSolve application. The following techniques are available:

Client Application Entry Point

Basic Entry Point

A client script should export a function that is the script's entry point. This function takes the reSolve context as the parameter.

const main = async resolveContext => {
...
}
export default main

To register the entry point, assign the path to the file that contains the entry point definition to the clientEntries configuration option:

clientEntries: ['client/index.js']

Use the resolveContext object to initialize a client library. The code samples below demonstrate how to configure the entry point for different client libraries.

@resolve-js/client:

import { getClient } from '@resolve-js/client'
const main = async resolveContext => {
  await new Promise(resolve => domready(resolve))
  const client = getClient(resolveContext)
  const { data } = await client.query({
    name: 'chat',
    aggregateIds: '*'
  })
  ...
}

@resolve-js/redux:

import { createResolveStore, ResolveReduxProvider } from '@resolve-js/redux'

const entryPoint = (clientContext) => {
  const store = createResolveStore(clientContext, {
    serializedState: window.__INITIAL_STATE__,
    redux: getRedux(),
  })
  const routes = getRoutes()
  render(
    <ResolveReduxProvider context={clientContext} store={store}>
      <BrowserRouter>{renderRoutes(routes)}</BrowserRouter>
    </ResolveReduxProvider>,
    document.getElementById('app-container')
  )
}
export default entryPoint

@resolve-js/react-hooks:

import { ResolveProvider } from '@resolve-js/react-hooks'
...
const entryPoint = (clientContext) => {
  const appContainer = document.createElement('div')
  document.body.appendChild(appContainer)
  render(
    <ResolveProvider context={clientContext}>
      <BrowserRouter>{renderRoutes(routes)}</BrowserRouter>
    </ResolveProvider>,
    appContainer
  )
}
export default

SSR Handlers

To use Server-Side Rendering (SSR) in your application, you need to implement one or more handlers that pre-render the client application's markup on the server.

An SSR handler is an asynchronous function that receives the resolveContext along with a request and response objects. As the result of its execution, an SSR handler should send a response that contains the rendered markup:

const ssrHandler = async (
  resolveContext,
  req,
  res
) => {
  ...
  const markupHtml =
    `<!doctype html>`
      `<html ${helmet.htmlAttributes.toString()}>` +
      ...
      '</html>'
  await res.end(markupHtml)
}

To enable server-side rendering, specify an array of server-side rendering scripts that target different environments in the clientEntries configuration section:

clientEntries: [
  'client/index.js',
  [
    'client/ssr.js',
    {
      outputFile: 'common/local-entry/ssr.js',
      moduleType: 'commonjs',
      target: 'node',
    },
  ],
  [
    'client/ssr.js',
    {
      outputFile: 'common/cloud-entry/ssr.js',
      moduleType: 'commonjs',
      target: 'node',
    },
  ],
]

For more information on these settings, refer to the Application Configuration article.

To serve SSR markup to the client, you need to register the live-require-handler.js API handler in the apiHandlers configuration section:

config.app.js:

...
apiHandlers: [
  {
    handler: {
      module: {
        package: '@resolve-js/runtime-base',
        import: 'liveRequireHandler',
      },
      options: {
        modulePath: './ssr.js',
        moduleFactoryImport: false
      }
    },
    path: '/:markup*',
    method: 'GET'
  }
],
...

HTTP API

A reSolve exposes HTTP API that you can use to send aggregate commands and query Read Models. The following endpoints are available.

Purpose Endpoint Method
Send a command http://{host}:{port}/api/commands POST
Query a Read Model http://{host}:{port}/api/query/{readModel}/{resolver} POST
Query a View Model http://{host}:{port}/api/query/{viewModel}/{aggregateIds} GET

Example

The code sample below demonstrates how you can implement JavaScript functions used to communicate with a reSolve server through its HTTP API:

const apiCommandsUrl = '/api/commands'
const apiQueryUrl = '/api/query'

const sendCommand = async ({
  aggregateName,
  aggregateId,
  type,
  payload,
  jwt,
}) => {
  await fetch(apiCommandsUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${jwt}`,
    },
    body: JSON.stringify({
      aggregateName,
      aggregateId,
      type,
      payload,
    }),
  })
}

const queryReadModel = async (readModelName, resolver, parameters, jwt) => {
  const requestUrl = `${apiQueryUrl}/${readModelName}/${resolver}`
  const res = await fetch(requestUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${jwt}`,
    },
    body: JSON.stringify(parameters),
  })
  return await res.json()
}

const queryViewModel = async (viewModelName, aggregateIds, jwt) => {
  const requestUrl = `${apiQueryUrl}/${viewModelName}/${aggregateIds.join(',')}`
  const res = await fetch(requestUrl, {
    method: 'GET',
    headers: {
      Authorization: `Bearer ${jwt}`,
    },
  })
  return await res.json()
}

For more information on the HTTP API, refer to the following help topic: API Reference.

You can extend a reSolve server's API with API Handlers. Refer to the following help topic for more information: API Handlers.

@resolve-js/client library

The @resolve-js/client library exposes an interface that you can use to communicate with the reSolve backend from JavaScript code. To initialize the client, call the library's getClient function. This function takes a reSolve context as a parameter and returns an initialized client object. This object exposes the following functions:

Function Description
command Sends an aggregate command to the backend.
query Queries a Read Model.
getStaticAssetUrl Gets a static file's full URL.
getOriginPath Returns an absolute URL within the application for the given relative path.
subscribe Subscribes to View Model updates.
unsubscribe Unsubscribes from View Model updates.

Example

The with-vanilajs template project demonstrates how to use the @resolve-js/client library to implement a frontend for a reSolve application in pure JavaScript.

@resolve-js/redux library

The reSolve framework includes the client @resolve-js/redux library used to connect a client React + Redux app to a reSolve-powered backend.

Use the following @resolve-js/redux library's hooks and Higher-Order Components (HOCs) to connect react components to the backend.

React Hooks

Function Name Description
useReduxCommand Creates a hook to execute a command.
useReduxReadModel Creates a hook to query a Read Model.
useReduxReadModelSelector Creates a hook to access a Read Model query result.
useReduxViewModel Creates a hook to receive a View Model's state updates and reactive events.
useReduxViewModelSelector Creates a hook to access a View Model's current state on the client.

Higher-Order Components

Function Name Description
connectViewModel Connects a React component to a reSolve View Model.
connectReadModel Connects a React component to a reSolve Read Model.
connectRootBasedUrls Fixes URLs passed to the specified props so that they use the correct root folder path.
connectStaticBasedUrls Fixes URLs passed to the specified props so that they use the correct static resource folder path.

Example

The shopping-list-redux-hoc example application demonstrates how to use the @resolve-js/redux library to implement a react-redux frontend for a reSolve application.

@resolve-js/react-hooks library

The @resolve-js/react-hooks library includes React hooks that you can use to connect React components to a reSolve backend. The following hooks are included:

Hook Description
useСlient Returns the @resolve-js/client library's client object.
useCommand Initializes a command that can be passed to the backend.
useCommandBuilder Allows a component to generate commands based on input parameters.
useViewModel Establishes a WebSocket connection to a reSolve View Model.
useQuery Allows a component to send queries to a reSolve Read Model or View Model.
useQueryBuilder Allows a component to generate queries based on input parameters.
useOriginResolver Resolves a relative path to an absolute URL within the application.
useStaticResolver Resolves a relative path to a static resource's full URL.

Example

The shopping-list-with-hooks example application demonstrates how to use the @resolve-js/react-hooks library to communicate with a reSolve backend.