ElementQL
is a query language specification and implementation to query a server for Web Elements source code in order to render them on the client.
Consider the following Web Element which uses React
const HelloElementQL = () => {
return React.createElement('div', null, 'Hello from ElementQL');
}
When embedded into a standard React
rendering process, the HelloElementQL
functional element will generate a div
containing the text Hello from ElementQL
.
The normal manner of sending the element to the browser is by packing it up into an Application
, in a large JavaScript
index.js
file, which gets attached to a index.html
with a script
tag and then gets sent by the server to the client.
The manner proposed by ElementQL
is to let the client request only the required elements at runtime from a server and receive only the particular element-specific module.
The NodeJS
server can be installed running the command
npm install @plurid/elementql-server
or
yarn add @plurid/elementql-server
The simplest ElementQL
server requires only to be started, the elements will be served from the default ./elements
path
// server.js
import ElementQLServer from '@plurid/elementql-server';
const server = new ElementQLServer();
server.start();
The server will then accept requests on the http://localhost:21100/elementql
URL for the elements in the ./elements
directory.
The ./elements
directory has a structure of folders with element-specific files: .js
, .jsx
, .ts
, .tsx
, or .css
. For example
.
|- server.js
|- elements
| |- HelloElementQL
| | |- index.js
| |-
|-
The ElementQLServer
Object can receive an options object
import {
ElementQLServerOptions,
} from '@plurid/elementql-server';
/** defaults */
const options: ElementQLServerOptions = {
protocol: 'https', /** default for production; for development: `'http'` */
domain: '', /** the domain for the server, e.g. example.com */
/** will be used to resolve elements relative and library imports */
port: 21100,
rootDirectory: process.cwd(),
buildDirectory: 'build', /** relative to the root directory */
nodeModulesDirectory: 'node_modules', /** relative to the root directory */
elementqlDirectory: '.elementql', /** relative to the build directory */
transpilesDirectory: 'transpiles', /** relative to the elementql directory */
elementsDirectories: [ /**/
'elements', /** relative to the build directory */
], /**/
libraries: {},
endpoint: '/elementql',
allowOrigin: ['*'],
allowHeaders: ['*'],
plugins: [ /**/
'minify', /** default for production; for development: `[]` */
], /**/
verbose: true,
open: true,
playground: false,
playgroundEndpoint: '/playground',
store: null,
metadataFilename: 'metadata.json',
};
In production, an ElementQL Client is recommended. In development/testing, the requests for elements can be made using the POST
method with a Content-Type
header of application/json
or application/elementql
. For example
JSON
curl http://localhost:21100/elementql \
-H "Content-Type: application/json" \
-v --data '{"elements":[{"name":"HelloElementQL"}]}'
ElementQL
curl http://localhost:21100/elementql \
-H "Content-Type: application/elementql" \
-v --data 'elements{HelloElementQL}'
In each element directory there can be an elementql.yaml
file with metadata specific to the element
# the element name derived from the directory name will be overwrriten with `OverwriteName`
name: OverwriteName
The elementql-server
for Go
is a go 1.14
module.
Considering the standard React
application, using ElementQL
involves
- creating an
elementql.yaml
configuration file,
---
globals:
react: React
react-dom: ReactDOM
origins:
elementql: http://localhost:21100/elementql
application: http://localhost:8005
bootloader: './source/index.js'
entry: './app/index.js'
- creating the
service-worker
,
const elementQLServiceWorker = './node_modules/@plurid/elementql/distribution/service-worker.js';
importScripts(elementQLServiceWorker);
- creating and running the
metabootloader
,
const metabootloader = require('@plurid/elementql/distribution/metabootloader').default;
metabootloader();
- creating and running
useLibraries
const {
libraries,
useLibraries,
} = require('@plurid/elementql');
const usedLibraries = {
react: libraries.react,
reactDom: libraries.reactDom,
};
const buildDirectory = 'build';
useLibraries({
libraries: usedLibraries,
buildDirectory,
});
- defining an
ElementQL
/JSON
request, - instantiating an
ElementQLClient
with theURL
for theElementQL
server endpoint, - and making the request with the
useEffect
,useState
standardReact
hooks, - or with the
useElementQL
custom hook.
import React, {
useEffect,
useState,
} from 'react';
import ElementQLClientReact, {
useElementQL,
} from '@plurid/elementql-client-react';
const elementQLClient = new ElementQLClientReact({
url: 'http://localhost:21100/elementql',
});
const ElementQLJSONRequest = {
elements: [
{
name: 'HelloElementQL',
},
],
};
const AppWithHook: React.FC<any> = () => {
const Elements = useElementQL(
elementQLClient,
ElementQLJSONRequest,
'json',
);
return (
<>
{Elements && (
<Elements.HelloElementQL />
)}
</>
);
}
const App: React.FC<any> = () => {
const [Elements, setElements] = useState<any>();
useEffect(() => {
let mounted = true;
const fetchElements = async () => {
const {
status,
Elements,
}: any = await elementQLClient.get(
ElementQLJSONRequest,
'json',
);
if (!status || !mounted) {
return;
}
setElements(Elements);
}
fetchElements();
return () => {
mounted = false;
}
}, []);
return (
<>
{Elements && (
<Elements.HelloElementQL />
)}
</>
);
}
export default App;
Uses Babel to transpile .js
and .jsx
element files.
Uses TypeScript to transpile .ts
and .tsx
element files.
Uses Terser to minify the element files.
@plurid/elementql • base
@plurid/elementql-client-react • react
client
@plurid/elementql-express • express
middleware
@plurid/elementql-server • NodeJS server
@plurid/elementql-server • Go server
@plurid/elementql-specification • specification
@plurid/elementql-org • documentation
import elementQLClientReact, {
elementQL
} from '@plurid/elementql-client-react';
const elementQLClient = elementQLClientReact({
url: 'https://api.example.com/elementql',
});
const DIV = elementQL`
import {
<Div>
}
`;
// gets a React Function Component
const Div = await elementQL.get(DIV);
@plurid/elementql-client-react uses @plurid/elementql-client under the hood to inject the script/style
and returns window.elementQL.Div which is a React function component
the React component should be exported wrapped around a HoC withElementQL which will make the component available on window
import {
ElementQL,
} from '@plurid/elementql-client-react';
const SomeProvidedElement = () => (
<ElementQL.Provider
elements={{
SomeElement: () => (<div>some element</div>),
}}
>
<ElementQL.Consumer>
{(context) => {
const SomeElement = context?.getElement('SomeElement');
if (!SomeElement) {
return (<></>);
}
return (<SomeElement />);
}}
</ElementQL.Consumer>
</ElementQL.Provider>
);