Skip to content

Commit

Permalink
Merge e0ffd8a into f161ea7
Browse files Browse the repository at this point in the history
  • Loading branch information
andrzejewsky committed Mar 17, 2021
2 parents f161ea7 + e0ffd8a commit 9b95721
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 9 deletions.
21 changes: 19 additions & 2 deletions packages/core/docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ module.exports = {
head: [
['link', { rel: 'icon', href: '/favicon.png' }]
],
configureWebpack: (config) => {
config.module.rules = config.module.rules.map(rule => ({
...rule,
use: rule.use && rule.use.map(useRule => ({
...useRule,
options: useRule.loader === 'url-loader' ?
/**
Hack for loading images properly.
ref: https://github.com/vuejs/vue-loader/issues/1612#issuecomment-559366730
*/
{ ...useRule.options, esModule: false } :
useRule.options
}))
}))
},
themeConfig: {
logo: 'https://camo.githubusercontent.com/48c886ac0703e3a46bc0ec963e20f126337229fc/68747470733a2f2f643968687267346d6e767a6f772e636c6f756466726f6e742e6e65742f7777772e76756573746f726566726f6e742e696f2f32383062313964302d6c6f676f2d76735f3062793032633062793032633030303030302e6a7067',
nav: [
Expand Down Expand Up @@ -173,12 +188,14 @@ module.exports = {
title: 'Advanced [WIP]',
collapsable: false,
children: [
['/advanced/architecture', 'Architecture'],
['/advanced/context', 'Application Context'],
['/advanced/calling-platform-api', 'Calling Platform API'],
['/advanced/server-middleware', 'Server Middleware'],
['/advanced/internationalization', 'Internationalization'],
['/advanced/performance', 'Performance'],
['/advanced/ssr-cache', 'SSR Cache'],
['/advanced/logging', 'Logging'],
['/advanced/architecture', 'Architecture']
['/advanced/logging', 'Logging']
]
},
{
Expand Down
25 changes: 25 additions & 0 deletions packages/core/docs/advanced/calling-platform-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Calling integration APIs

## How do integrations work?

In Vue Storefront, single integration has its own sort of software development kit (SDK) library. This library is named API-client and contains a set of functions. Each function is dedicated to one action, endpoint or a feature eg. `getProduct`, `loadCart` `addToCart`. For example, when you call `getProduct`, it will request the integration API and return its response in the original format of a given platform.

## Accessing integration methods on the frontend

To access API-client functions, you can use the composable function `useVSFContext`:

```ts
// each platform has different tag to access its methods, eg $spryker, $storyblok etc.
const { $ct } = useVSFContext();

$ct.api.getProduct({ id: 1 })
```

In the example above we access the API-client for `commercetools` and call `getProduct` function. Each integration has a dedicated tag name - for more information see [context docs](/advanced/context).



## Extending Integrations

Sometimes, it's necessary to override the original behavior for either API-client or even an entire request that comes from an external platform.
The Vue Storefront also provides a possibility to do this by using [middleware extensions](/advanced/server-middleware)
146 changes: 146 additions & 0 deletions packages/core/docs/advanced/server-middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Server middleware

## What is Vue Storefront Middleware and why we need it?

The Vue Storefront middleware is an Express proxy that takes the requests from the front-end, translates them to a given integration, and calls related API-client.

We have implemented it for a variety of reasons.

First of all, it allows us to provide a proven way of extensibility. As a developer, you have control of the requests and responses in the given platform with [extensions](/advanced/server-middleware.html#extending-middleware))

All platform credentials are stored only on the server side and not exposed to the frontend part of your application.

Performance optimizations - since we moved the networking layer to the server-side, the final code shipped to the browser is way smaller, which impacts the initial loading time.

## How it works (in a nutshell)

The way it works represents the following diagram:

<center>
<img src="../images/middleware-diagram.jpg" alt="API Middleware Diagram" />
</center>

The API-client is being called only on the middleware, but you still can access it on the front-end side - how is that possible?

When you access an API-client on the front-end side, you are accessing actually a stub, instead of a real API-client instance. This stub makes a call to the middleware ([Remote Procedure Call](https://en.wikipedia.org/wiki/Remote_procedure_call)), and asks for loading a specific integration and executing a function you are asking for.

For example, the following code:
```js
context.$ct.getProduct({ id 1})
```

Generates the request to our middleware:
- `POST / api/ct/getProduct` - a http call, where `ct` is a tag name of integation and `getProduct` is the name of a function needs to be called
- `http body` - the body of HTTP request we are sending array of arguments

Middleware reads tag name of integration and the function name that needs to be called, executes it, and sends a response back to the browser as if it was transferred using a direct connection.

## Configuration

When it comes to configuration, middleware has a dedicated config called `middleware.config.js` that contains a section with integrations(`integrations`) along with their credentials and other options.

```js
module.exports = {
integrations: {
<TAG NAME>: {
location: '@<integration-package>/server',
configuration: {}
extensions: (extensions) => extensions,
customQeries: {}
}
}
};
```

Each entry under the `integrations` section starts with a tag name of given integration, and contains an object with the following fields:

- `location` - points to the package of the API-client, related to given integration (server entry point)
- `configuration` - contains a configuration of given integration, such as credentials and others
- `extensions` - a function that returns a extensions (jump to the next section)
- `customQueries` - section that contains custom queries (graphql only)

## Extending Integrations

<center>
<img src="../images/middleware-extensions.jpg" alt="Middleware Extensions" />
</center>

Middleware allows you to inject into the lifecycle of the entire network flow, starting with configuring a connection and ending with a final response. To use those things, we created an extension feature.

You can define as many extensions as you want. Remember they are always correlated with a specific API-client (integration). How do they look like? Each extension has the following structure:

```js
const extension = {
name: 'extension-name',
extendApiMethods: {
getProduct: async () => { /* ... */ }
},
hooks: (req, res) => {
return {
beforeCreate: ({ configuration }) => configuration,
afterCreate: ({ configuration }) => configuration,
beforeCall: ({ configuration, callName, args }) => args,
afterCall: ({ configuration, callName, args, response }) => response
}
}
}
```

- `name` - defines the unique name of an extension
- `extendApiMethods` - overrides the original functions from API-client
- `hooks` - defines lifecycle hooks of API-client
- `hooks:beforeCreate` - called before API-client creates a connection, takes the given configuration as an argument, and must return the configuration. Here you can attach something else to the configuration or even change it.
- `hooks:afterCreate` - Similar to the previous one, but called after the connection has been created. It also returns a configuration and you can change it.
- `hooks:beforeCall` - called before each API-client function. We have access to the configuration, function name, and its arguments. This function must return the arguments and based on the input parameters we can change it.
- `hooks:afterCall` - called after each API-client function.We have access to the configuration, function name, and its arguments. This function must return the response and based on the input parameters we can attach something to it.


To register a created extension, we have to add it do the middleware config file:

```js
module.exports = {
integrations: {
<TAG NAME>: {
location: '@<integration-package>/server',
configuration: {}
extensions: (extensions) => [
...extensions,
{
name: 'our-extension'
hooks: () => { /* ... */}
}
],
customQeries: {}
}
}
};
```

## Separating middleware from Nuxt

By default, Vue Storefront middleware is running within the Nuxt.js process. Sometimes there is a need to disconnect it from the app and run it as a separate and independent instance (process).

Since it's just an Express application, you can do this, by creating a `middleware.js` file:

```js
const { createServer } = require('@vue-storefront/middleware');
const { integrations } = require('./middleware.config');

const app = createServer({ integrations });

app.listen(8181, () => {
console.log('Middleware started');
});
```

Now, when you run this using Node, your middleware should work separately.

Additionally, you need to remove the middleware module entry from the `nuxt.config.js` and configure the domain, where your middleware is settled.

```js
export default {
publicRuntimeConfig: {
middlewareUrl: 'https://api.commerce.com'
}
}
```
10 changes: 9 additions & 1 deletion packages/core/docs/general/key-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ const { $ct } = useVSFContext()

You can read more about Vue Storefront Context [here](/advanced/context)


## Middleware

When it comes to the networking layer, Vue Storefront uses a middleware that is a bridge between front-end and other backends (eCommerce or 3rd party services). The front-end always calls middleware that is redirecting requests to correlated destinations. It allows developers to implement custom logic to inject into the lifecycle of the requests or even create custom API endpoints if needed.

You can read more about Vue Storefront Middleware on the [Server Middleware](/advanced/server-middleware) page.


## Integrations

Even though high-level APIs are the same for all Vue Storefront integrations they're different on the low level (data formats, search params). Check the docs of a specific platform on the left side under "eCommerce integrations" tab to learn about them.
Even though high-level APIs are the same for all Vue Storefront integrations, they are different on the low level (data formats, search params). Check the documentation for a specific platform to learn more.
44 changes: 44 additions & 0 deletions packages/core/docs/guide/composables.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,47 @@ watch(() => ({...error.value}), (error, prevError) => {
In this example, we are using `useUiNotification` - a composable that handles notifications state. You can read more about it in the API reference.

[//]: # 'TODO: This should be added to API reference'

### How to customize graphql queries?

If your integration uses GraphQL API, you may need to change the default query that is being sent to fetch the data. That's quite a common case and Vue Storefront also provides the mechanism for this.
Since the communication with the API goes through our middleware, all queries also are defined there.
To customize or even totally override the original (default) queries you need to follow two steps.
Firstly, you need to use a dedicated parameter: `customQuery` that tells the app, what to do with a query.
This parameter is an object that has a name of the queries as keys, and the name of the queries function under the values.
```ts
const { search } = useProduct();
search({ customQuery: { products: 'my-products-query' } });
```
In the example above, we are changing `products` query, and our function that will take care of this overriding is `my-products-query`. As a second step, we need to define that function.
Each custom query lives in the `middleware.config.js`, so it's the place where we should define `my-products-query`:

```js
module.exports = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: { /* ... */ },
customQueries: {
'my-products-query': ({ query, variables }) => {
variables.locale = 'en'
return { query, variables }
}
}
}
}
};
```

The custom query function always has in the arguments the default query and default variables and must return the query and its variables as well. In the body you can do anything you want with those parameters - you can override them or even change to the new ones.
Binary file added packages/core/docs/images/middleware-diagram.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6345,9 +6345,9 @@ detect-newline@^2.1.0:
integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=

detect-node@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
version "2.0.5"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79"
integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==

devalue@^2.0.1:
version "2.0.1"
Expand Down Expand Up @@ -6610,9 +6610,9 @@ ejs@^3.0.2:
jake "^10.6.1"

electron-to-chromium@^1.3.649:
version "1.3.689"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.689.tgz#0f4082467c109844b79a7b32a2649c9ab6a6c822"
integrity sha512-WCn+ZaU3V8WttlLNSOGOAlR2XpxibGre7slwGrYBB6oTjYPgP29LNDGG6wLvLTMseLdE+G1vno7PfY7JyDV48g==
version "1.3.690"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.690.tgz#54df63ec42fba6b8e9e05fe4be52caeeedb6e634"
integrity sha512-zPbaSv1c8LUKqQ+scNxJKv01RYFkVVF1xli+b+3Ty8ONujHjAMg+t/COmdZqrtnS1gT+g4hbSodHillymt1Lww==

elliptic@^6.5.3:
version "6.5.4"
Expand Down

0 comments on commit 9b95721

Please sign in to comment.