Overview
Composition lets you build custom APIs using function pipelines that create multiple external resources as a single object. Instead of manually creating Deployments, Services, ConfigMaps, and other resources individually, you can use a custom API to create all the necessary resources.
How compositions work​
The composition workflow consists of three components:
- Composite Resource Definitions (
XRDs) - define the API schema for the custom API you need to create your resources. - Compositions - the pipeline of logic to declare how to create those resources
 - Composite Resources (
XRs) - the resources created when users request them from your custom API. 
These components work together to create your resources as a single Kubernetes object for the control plane to manage.
Operational workflow​
- Platform team defines the API
 
The XRD is the schema for your custom API. If your custom API interacts with your cloud provider to create an Application deployment, your XRD can define:
- The API name (kind:App)
 - Necessary fields and what fields can accept user input
 - Validation and default values
 
Once the platform team creates the XRD, they apply it to their Crossplane control plane.
- Platform team implements the API
 
The Composition defines what happens when someone uses your custom API. The functions (written in a standard programming language) in the composition:
- Extract user input from the Composite Resource (XR)
 - Create the actual resources
 - Configure the relationships between resources
 
Once the platform team creates the Composition, they apply it to their Crossplane control plane.
- User applies the API
 
The Composite Resource (XR) is the request for the Composition to create the defined resources and any user input that may be required
- Control plane creates the resources
 
Crossplane constantly watches for new requests and when the user applies the XR, Crossplane takes action:
- Crossplane finds the related Composition that matches the XR
 - Executes the functions in the Composition pipeline
 - Creates the generated resources
 
Example​
Let's work from the XR to the XRD to understand what's happening.
- XR
 - Composition
 - XRD
 
The XR below is an example of what a user would need to create an App. The
fields available to edit are fields that the platform team determines. In this
case, the user can choose the namespace of the App, the name, and the
image.
Namespacing resources is helpful to segment your infrastructure. The user can
deploy to the default namespace and chose an nginx image to deploy.
apiVersion: example.crossplane.io/v1
kind: App
metadata:
  namespace: default
  name: my-app
spec:
  image: nginx
The Composition below is the logic pipeline between the XR and the XRD. This
example uses function-python to create the function logic and declares what to
compose based on Observed resources and Desired resources for the kind
the XR requests.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: app-python
spec:
  compositeTypeRef:
    apiVersion: example.crossplane.io/v1
    kind: App
  mode: Pipeline
  pipeline:
  - step: create-deployment-and-service
    functionRef:
      name: crossplane-contrib-function-python
    input:
      apiVersion: python.fn.crossplane.io/v1beta1
      kind: Script
      script: |
        def compose(req, rsp):
            observed_xr = req.observed.composite.resource
            rsp.desired.resources["deployment"].resource.update({
                "apiVersion": "apps/v1",
                "kind": "Deployment",
                "metadata": {
                  "labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
                },
                "spec": {
                    "replicas": 2,
                    "selector": {"matchLabels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}},
                    "template": {
                      "metadata": {
                        "labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
                      },
                      "spec": {
                        "containers": [{
                          "name": "app",
                          "image": observed_xr["spec"]["image"],
                          "ports": [{"containerPort": 80}]
                        }],
                      },
                    },
                },
            })
            observed_deployment = req.observed.resources["deployment"].resource
            if "status" in observed_deployment:
              if "availableReplicas" in observed_deployment["status"]:
                rsp.desired.composite.resource.get_or_create_struct("status")["replicas"] = observed_deployment["status"]["availableReplicas"]
              if "conditions" in observed_deployment["status"]:
                for condition in observed_deployment["status"]["conditions"]:
                  if condition["type"] == "Available" and condition["status"] == "True":
                    rsp.desired.resources["deployment"].ready = True
            rsp.desired.resources["service"].resource.update({
                "apiVersion": "v1",
                "kind": "Service",
                "metadata": {
                  "labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
                },
                "spec": {
                  "selector": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
                  "ports": [{"protocol": "TCP", "port": 8080, "targetPort": 80}],
                },
            })
            observed_service = req.observed.resources["service"].resource
            if "spec" in observed_service and "clusterIP" in observed_service["spec"]:
              rsp.desired.composite.resource.get_or_create_struct("status")["address"] = observed_service["spec"]["clusterIP"]
              rsp.desired.resources["service"].ready = True        
Finally, the XRD below is an example of the schema that Crossplane needs for
this kind.
apiVersion: apiextensions.crossplane.io/v2alpha1
kind: CompositeResourceDefinition
metadata:
  name: apps.example.crossplane.io
spec:
  scope: Namespaced
  group: example.crossplane.io
  names:
    kind: App
    plural: apps
  versions:
  - name: v1
    served: true
    referenceable: true
    schema:
     openAPIV3Schema:
       type: object
       properties:
        spec:
          type: object
          properties:
            image:
              description: The app's OCI container image.
              type: string
          required:
          - image
        status:
          type: object
          properties:
            replicas:
              description: The number of available app replicas.
              type: integer
            address:
              description: The app's IP address.
              type: string
Next steps​
The next pages in this section go into detail about each of these components.