Frontend
This document describes techniques that you can use to implement a frontend for a reSolve application. The following techniques are available:
- HTTP API - An HTTP API exposed by a reSolve server.
- @resolve-js/client library - A higher-level JavaScript library used to communicate with a reSolve server.
- @resolve-js/redux library - A library used to connect a React + Redux component to reSolve.
- @resolve-js/react-hooks library - A hook-based library used to connect React components to reSolve.
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.