# Storefront API Modules

We designed the Storefront API (SFAPI) upon fully modular architecture. We composed the modules engine with the Inversion of Control principle. In this particular case it means that each module has full control over core SFAPI features, including:

  • GraphQL schema,
  • GraphQL middlewares,
  • ElasticSearch schema,
  • REST API Endpoints,
  • REST API Middlewares.

# Default modules

The default API implementation - packages/default-vsf is compatible with Vue Storefront. You might want to implement the API in with totally different formats creating a new module based on one of the default modules or the boilerplates: template-module and sample-api

# default-catalog module

This module is in charge of providing the standard GraphQL Schema and ElasticSearch entities. This module is for providing the Vue Storefront compatibility - including integrations with Magento1, Magento2, Spree and other platforms. You might want to use this module to implement custom backend integration. If your backend platform uses different data formats, it might be easier to comment out the default module from modules entry point and create a new one.

# default-vsf module

This module implements a REST API covering most of the operations eCommerce can have - including user, cart, order, review and other modules. The cool part is that it's compatible with Vue Storefront and most of the existing integrations we have for it. If you'd like to integrate this API with your custom backend, you might want to follow the custom backend integration tutorial. This module uses a platform drivers. You may want to implement your custom driver. The other way around is to disable default-vsf module and create a new API from scratch, using the simple-api.

# default-img module

The image resizer which is compatible with Vue Storefront and default-catalog module and the platform concept. It takes the baseUrl from the config[config.platform] section by default and based on that generates the thumbnail or crops the picture.

# Where can I find my modules?

Modules are located in src/modules however, you can easily load npm based modules just by referencing them in modules entry point. The same way you can disable the default e-Commerce modules that are provided within the default-catalog, default-vsf and default-img folders.

# How module interface looks like?

The interfaces must be referenced prior to use in the src/modules/index.ts entrypoint:

import { DefaultVuestorefrontApiModule } from '@storefront-api/default-vsf'
import { DefaultCatalogModule } from '@storefront-api/default-catalog'
import * as magento2 from '@storefront-api/platform-magento2'

export default [
  DefaultVuestorefrontApiModule({
    platform: {
      name: 'magento2',
      platformImplementation: magento2
    }
  }),
  DefaultCatalogModule,
  ...
]

Each module file is returning the StorefrontApiModule structure:

export const TemplateModule: StorefrontApiModule = new StorefrontApiModule({
  key: 'template-module',

  /** Return the GraphQL schema and resolvers */
  initGraphql: ({ config, db, app }: StorefrontApiContext): GraphqlConfiguration => {
    return {
      resolvers,
      schema,
      hasGraphqlSupport: true
    }
  },

  /** Return ElasticSearch mappings - you might extend existing entities (deepMerge will be executed on the types) */
  loadMappings: ({ config, db, app }: StorefrontApiContext): ElasticSearchMappings => {
    return {
      schemas: {
        'custom': loadSchema(path.join(__dirname, 'elasticsearch'), 'custom', config.get('elasticsearch.apiVersion'))
      }}
  },

  /** Here you can just extend the default Express.js application */
  initApi: ({ config, db, app }: StorefrontApiContext): void => {
    let api = Router();

    // mount the order resource
    api.use('/version', version({ config, db }));
    registerExtensions({ app, config, db, registeredExtensions: config.get('modules.templateModule.registeredExtensions'), rootPath: path.join(__dirname, 'api', 'extensions') })

    // api router
    app.use('/template', api);
  }
})

Please note: You don't have to implement all the methods. If your module is adding some graphql entities - please feel free to implement initGraphql and leave the other functions un-implemented.

# How the default module structure looks like?

There is no required module structure, however typically the modules consist the following folder structure (example):

|____graphql                        # graphql resolvers and schema
| |____resolvers.js                 # resolvers.js and schema.js returns the array of typedefs and resolvers using fileLoader()
| |____schema.js
| |____hello                        # you might want to have your schema in subfolders **.* is used for loading it
| | |____schema.graphqls
| | |____resolver.js
|____api                            # typically the API is divided into subfolders as well
| |____version.js
| |____extensions
|____index.ts
|____elasticsearch                  # json objects compatbible with Elastic Mappings
| |____elastic.schema.custom.json

# How can I query the ElasticSearch?

All the infrastructure is there. You might want to check how default-catalog is querying the data. However, this example is pretty much complicated as it's using the special Query adapters for advanced filtering.

The simplest example was presented in the template-module:

    testElastic: async (_, { sku }, context, rootValue) => {
      const client = es.getClient(config)
      const esQuery = es.adjustQuery({
        index: 'vue_storefront_catalog', // current index name
        type: 'product',
        body: bodybuilder().filter('terms', 'visibility', [2, 3, 4]).andFilter('term', 'status', 1).andFilter('terms', 'sku', sku).build()
      }, 'product', config)
      const response = es.getResponseObject(await client.search(esQuery)).hits.hits.map(el => { return el._source })
      if (response.length > 0) return response[0]; else return null
    }

As you can see - there is bodybuilder included and the elastic client - ready for your commands!

# How do I create a new module?

Please read the tutorial.md. The simplest way is to just copy the template-module.

# Is there a way to extend the default modules?

There is no explicit way to extend the existing module other than just creating a new module and using the composition (importing the elements you'd like to reuse) from the base/fallback module.