Custom business logic ===================== .. contents:: Table of contents :backlinks: none :depth: 1 :local: For the backends of most apps, you may have to implement custom business logic to complement the CRUD and real-time API provided by GraphQL engine. Depending on the nature of the use case and its position vis-a-vis GraphQL engine/Postgres, different avenues are recommended for introducing such business logic in your app's backend: - **Pre-CRUD**: :ref:`remote-schemas` - **Post-CRUD**: :ref:`event-triggers` - :ref:`derived-data` .. thumbnail:: /img/graphql/core/business-logic/custom-business-logic.png .. _remote-schemas: Custom resolvers in remote schemas ---------------------------------- Merging remote schemas is ideal for adding "pre-CRUD" business logic (*logic to be run before you invoke GraphQL engine's GraphQL API to insert/modify data in Postgres*) or custom business logic that is not part of your GraphQL engine schema. Here are some use-cases where remote schemas are ideal: - Customizing mutations (e.g. running validations before inserts) - Supporting features like payments, etc. and providing a consistent interface to access them i.e. behind the GraphQL engine’s API - Fetching disparate data from other sources (e.g. from a weather API or another database) To support these kinds of business logic, a custom GraphQL schema with resolvers that implement said business logic is needed (*see link below for boilerplates*). This remote schema can then be merged with GraphQL engine's schema using the console. Here's a reference architecture diagram for such a setup: .. thumbnail:: /img/graphql/core/schema/schema-stitching-v1-arch-diagram.png For more details, links to boilerplates for custom GraphQL servers, etc. please head to :doc:`../remote-schemas/index`. .. _event-triggers: Asynchronous business logic / Events triggers --------------------------------------------- "post-CRUD" business logic (*follow up logic to be run after GraphQL engine's GraphQL API has been used to insert or modify data in Postgres*) typically tends to be asynchronous, stateless and is triggered on changes to data relevant to each use case. E.g. for every new user in your database, you may want to send out a notification. This business logic is triggered for every new row in your ``users`` table. GraphQL engine comes with built-in events triggers on tables in the Postgres database. These triggers capture events on specified tables and then invoke configured webhooks, which contain your business logic. If your business logic is stateful, it can even store its state back in the Postgres instance configured to work with GraphQL engine, allowing your frontend app to offer a reactive user experience, where the app uses GraphQL subscriptions to listen to updates from your webhook via Postgres. .. thumbnail:: /img/graphql/core/event-triggers/database-event-triggers.png Event triggers are ideal for use cases such as the following: - Notifications: Trigger push notifications and emails based on database events - ETL: Transform and load data into external data-stores. - E.g. transform data from Postgres and populate an Algolia index when a product is inserted, updated or deleted. - Long-running business logic: - Provision some infrastructure - Process multimedia files - Background jobs - Cache/CDN purge: invalidate/update entries in your cache/CDN when the underlying data in Postgres changes. For more information on event triggers and how to set them up, please see :doc:`../event-triggers/index`. .. _derived-data: Derived data / Data transformations ----------------------------------- For some use cases, you may want to transform your data in Postgres or run some predetermined function on it to derive another dataset (*that will be queried using GraphQL engine*). E.g. let's say you store each user's location data in the database as a ``point`` type. You are interested in calculating the distance (*say the haversine distance*) between each set of two users i.e. you want this derived dataset: .. list-table:: :header-rows: 1 * - user_id_1 - user_id_2 - distance between users * - 12 - 23 - 10.50 * - 12 - 47 - 76.00 The easiest way to handle these kinds of use cases is to create a view, which encapsulates your business logic (*in our example, calculating the distance between any two users*), and query your derived/transformed data as you would a table using GraphQL engine (*with permissions defined explicitly for your view if needed*). For more information on how to do this, please see :doc:`../queries/derived-data`.