Previously, you bundled your apps into API products. Now, you configure the backend and frontend of your developer portal to serve the API products to your end users.

Before you begin

Complete the API product tutorial.

Create the backend portal

Create a Portal that configures the host domain on which you want to expose your ApiProducts to end users.

Step 1: Create the portal

  1. Create a Gloo Portal custom resource.

         kubectl apply -f- <<EOF
       apiVersion: apimanagement.gloo.solo.io/v2
       kind: Portal
       metadata:
         name: developer-portal
         namespace: gloo-mesh-addons
       spec:
         visibility:
           public: true
           privateAPILabels:
             portal-visibility: private
         domains:
           - "developer.example.com"
         portalBackendSelectors:
           - selector:
               labels:
                 app: gloo-mesh-portal-server
         usagePlans:
           - name: bronze
             displayName: "Bronze Plan"
             description: "A basic usage plan"
           - name: silver
             description: "A better usage plan"
           - name: gold
             description: "The best usage plan!"
         apis:
           - labels:
               portal: dev-portal 
       EOF
      

    Review the following table to understand this configuration.

    FieldDescription
    metadataNote that the Portal is created in the same namespace as the portal server, gloo-system. If your Portal is in a different namespace, you must create a Kubernetes ReferenceGrant to let the Portal refer to the portal server’s Service.
    visibilityDecide whether the ApiProducts in the portal are visible to all users (public APIs) or if the ApiProducts are hidden until the user successfully authenticates (private APIs). The default value is false to set up private APIs. The example sets the value to true so that any user can view the ApiProducts
    domainsSet the host domain that you want to expose the backend portal on. Note that this is not the same as the URL that your end users access the frontend portal on. The example sets developer.example.com.
    apiProductsSelect the ApiProducts to include in the portal. The example uses the label selector expose-portal: "true", which you set in the object metadata of the ApiProducts that you previously created.
    portalBackendSelectorsSelect the backing portal server that you deployed when you installed Gloo Gateway.
  2. Check that Gloo automatically generated a PortalConfig resource. The PortalConfig is an internal resource that Gloo uses to combine the stitched schema of the ApiDoc, the portal metadata of each ApiProduct, and the other details such as visibility and host domain for your Portal.

      kubectl describe portalconfigs -n gloo-mesh-addons
      

    Example output:

       apiVersion: v1
    items:
    - apiVersion: internal.gloo.solo.io/v2
      kind: PortalConfig
      metadata:
        annotations:
          cluster.solo.io/cluster: cluster-1
        creationTimestamp: "2023-04-06T21:30:41Z"
        generation: 1
        labels:
          context.mesh.gloo.solo.io/cluster: cluster-1-portal
          context.mesh.gloo.solo.io/namespace: gloo-mesh-gateways
          context.mesh.gloo.solo.io/workspace: cluster-1-portal
          gloo.solo.io/parent_cluster: cluster-1-portal
          gloo.solo.io/parent_group: ""
          gloo.solo.io/parent_kind: Namespace
          gloo.solo.io/parent_name: gloo-mesh-gateways
          gloo.solo.io/parent_namespace: ""
          gloo.solo.io/parent_version: v1
          reconciler.mesh.gloo.solo.io/name: translator
        name: developer-portal-gloo-mesh-addons-cluster-1-portal
        namespace: gloo-mesh-addons
        resourceVersion: "1985686"
        uid: 59fe5508-d839-4720-b6ae-59347664e3bb
      spec:
        apis:
        - apiSchema:
            cluster: cluster-1-portal
            name: tracks-rt-stitched-openapi-cluster-1-portal-gloo-mesh-gateways-cluster-1-portal
            namespace: gloo-mesh-addons
          contact: support@example.com
          description: REST API for Catstronauts to retrieve data for tracks, authors
         and modules.
          license: license info
          routeTable:
            cluster: cluster-1-portal
            name: tracks-rt
            namespace: gloo-mesh-gateways
          termsOfService: You must authenticate to use this API! And other Terms of Service.
          title: Catstronauts REST API
        - apiSchema:
            cluster: cluster-1-portal
            name: petstore-rt-stitched-openapi-cluster-1-portal-gloo-mesh-gateways-cluster-1-portal
            namespace: gloo-mesh-addons
          contact: support@example.com
          description: Totally awesome API for all things pets!
          license: license info
          routeTable:
            cluster: cluster-1-portal
            name: petstore-rt
            namespace: gloo-mesh-gateways
          termsOfService: You must authenticate to use this API! And other Terms of Service.
          title: Petstore REST API
        domains:
        - developer.example.com
        portalRef:
          cluster: cluster-1-portal
          name: developer-portal
          namespace: gloo-mesh-addons
        public: true
    kind: List
    metadata:
      resourceVersion: ""
      selfLink: ""
    

Step 2: Create a route to the portal

  1. Create a virtual gateway that you use to expose the developer portal. The following example selects the default ingress gateway that you deployed during your Gloo Mesh Gateway installation. For more examples such as HTTPS/TLS, see Configure gateway listeners.

      kubectl apply -f- <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: VirtualGateway
    metadata:
      name: istio-ingressgateway-portal
      namespace: gloo-mesh-gateways
    spec:
      listeners:
        - allowedRouteTables:
            - host: developer.example.com
            - host: portal.example.com
          http: {}
          port:
            number: 80
      workloads:
        - selector:
            cluster: $CLUSTER_NAME
            labels:
              istio: ingressgateway
    EOF
      
  2. Create a route table for the developer portal. The following route table creates routes for all the portal APIs so that you can interact with your developer portal.

      kubectl apply -f - <<EOF    
    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: dev-portal-rt
      namespace: gloo-mesh-gateways
    spec:
      hosts:
      - developer.example.com
      virtualGateways:
      - name: istio-ingressgateway-portal
        namespace: gloo-mesh-gateways
      defaultDestination:
        port:
          number: 8080
        ref:
          name: gloo-mesh-portal-server
          namespace: gloo-mesh-addons
          cluster: $CLUSTER_NAME
      http:
        #
        # Portal server routes that require authentication.
        #
        - forwardTo: {}
          name: authn
          labels:
            oauth: "true" # Use this label to apply an OAuth external auth policy
            route: portal-api
          matchers:
            #
            # /v1/me
            - uri:
                prefix: /v1/me
              method: OPTIONS
            - uri:
                prefix: /v1/me
              method: GET
              headers:
                - name: Authorization
                  value: "Bearer.*"
                  regex: true
            #
            # /v1/apis
            - uri:
                prefix: /v1/apis
              method: GET
              headers:
                - name: Authorization
                  value: "Bearer.*"
                  regex: true
            #
            # /v1/usage-plans
            - uri:
                prefix: /v1/usage-plans
              method: GET
              headers:
                - name: Authorization
                  value: "Bearer.*"
                  regex: true
            #
            # /v1/api-keys
            - uri:
                prefix: /v1/api-keys
              method: GET
              headers:
                - name: Authorization
                  value: "Bearer.*"
                  regex: true
            - uri:
                prefix: /v1/api-keys
              method: POST
              headers:
                - name: Authorization
                  value: "Bearer.*"
                  regex: true
            - uri:
                prefix: /v1/api-keys
              method: DELETE
              headers:
                - name: Authorization
                  value: "Bearer.*"
                  regex: true
        #
        # Portal server routes that are public and do not require authentication.
        #
        - forwardTo: {}
          name: no-auth
          labels:
            route: portal-api
          matchers:
            - uri:
                prefix: /v1/apis
              method: GET
            - uri:
                prefix: /v1/usage-plans
              method: GET
            - uri:
                prefix: /v1/api-keys
              method: GET
            #
            # Allow OPTION requests without authentication.
            #
            - uri:
                prefix: /v1/api-keys
              method: OPTIONS
            - uri:
                prefix: /v1/usage-plans
              method: OPTIONS
            - uri:
                prefix: /v1/apis
              method: OPTIONS
    EOF
      
  3. For local development and testing, apply a CORS policy. This way, you can access the portal frontend that you set up later from your localhost.

      kubectl apply -f -<<EOF
    apiVersion: security.policy.gloo.solo.io/v2
    kind: CORSPolicy
    metadata:
      name: dev-portal-cors
      namespace: gloo-mesh-gateways
    spec:
      applyToRoutes:
        - route:
            labels:
              route: portal-api
      config:
        allowCredentials: true
        allowHeaders:
          - "Content-Type"
          - "api-key"
          - "id_token"
          - "Authorization"
          - "Access-Control-Allow-Origin"
        allowMethods:
          - GET
          - POST
          - DELETE
          - PUT
          - OPTIONS
        allowOrigins:
          - prefix: http://localhost
          - prefix: http://127.0.0.1
          - prefix: https://developer.example.com
          - prefix: http://portal.example.com
    EOF
      
  4. Send a request to the developer portal to list the APIs that you have access to. Note that because the petstore API is set to private, only the tracks API is returned in your CLI output. To view the private petstore APIs, you must set up authentication and authorization. For more information, see Secure the portal.

      curl -vik --resolve developer.example.com:80:$INGRESS_GW_IP http://developer.example.com:80/v1/apis
      

    Example output:

      * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    HTTP/1.1 200 OK
    < content-type: application/json
    content-type: application/json
    < date: Fri, 14 Apr 2023 13:57:02 GMT
    date: Fri, 14 Apr 2023 13:57:02 GMT
    < content-length: 387
    content-length: 387
    < x-envoy-upstream-service-time: 0
    x-envoy-upstream-service-time: 0
    < server: istio-envoy
    server: istio-envoy
    
    < 
    [{"apiProductDisplayName":"Catstronauts Course Tracks","apiProductId":"tracks","apiVersions":[{"apiId":"tracks-v1","apiVersion":"v1","contact":"support@example.com","customMetadata":{"compatibility":"None"},"description":"REST API for Catstronauts to retrieve data for tracks, authors and modules.","license":"License info, such as MIT","lifecycle":"Supported","termsOfService":"You must authenticate to use this API! And other Terms of Service.","title":"Catstronauts REST API","usagePlans":["bronze","gold","silver"]}]},{"apiProductDisplayName":"Pet Store","apiProductId":"petstore","apiVersions":[{"apiId":"petstore-v1","apiVersion":"v1","contact":"support@example.com","customMetadata":{"compatibility":"None"},"description":"Totally awesome API for all things pets!","license":"License info, such as MIT","lifecycle":"Supported","termsOfService":"You must authenticate to use this API! And other Terms of Service.","title":"Pet Store REST API","usagePlans":["bronze","gold","silver"]}]}]
    
    * Connection #0 to host developer.example.com left intact
      
  5. Open the Gloo UI. The Gloo UI is served from the gloo-mesh-ui service on port 8090. You can connect by using the meshctl or kubectl CLIs.

    • meshctl: For more information, see the CLI documentation.
        meshctl dashboard
        
    • kubectl:
      1. Port-forward the gloo-mesh-ui service on 8090.
          kubectl port-forward -n gloo-mesh svc/gloo-mesh-ui 8090:8090
          
      2. Open your browser and connect to http://localhost:8090.
  6. From the menu, navigate to the APIs > Portals page.

    Figure: Gloo UI view of portals
    Figure: Gloo UI view of portals
  7. Click your portal and review the Published APIs and Usage Plans of the portal.

Figure: Gloo UI detailed view of portals
Figure: Gloo UI detailed view of portals

Good job! You set up the backend of your developer portal. The backend means that your ApiProducts are exposed on a backend-facing host, developer.example.com, that your internal services use to route and protect requests to the backing API services.

Next, you can set up a frontend portal.

Create the frontend portal

The frontend portal is the web user interface (UI) application that your end users access. In the portal, your end users can discover and authenticate to use your ApiProducts.

Gloo provides a sample React app that you can use as a starting point to develop your own frontend application. This frontend app displays the information that your Portal resource pulls together: the API products and usage plans that you want to expose, along with additional metadata. Because this information is controlled by the Portal resource, you don’t have to update the frontend app as often after the initial setup.

  1. Deploy the dev-portal-starter sample React app. For more information, see the GitHub project. Notice that the portal server URL environment variable is set with the developer.example.com route that you previously created for the backend portal.

      kubectl apply -n gloo-mesh-addons -f- <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: portal-frontend
      namespace: gloo-mesh-addons
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: portal-frontend
      namespace: gloo-mesh-addons
      labels:
        app: portal-frontend
        service: portal-frontend
    spec:
      ports:
        - name: http
          port: 4000
          targetPort: 4000
      selector:
        app: portal-frontend
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: portal-frontend
      namespace: gloo-mesh-addons
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: portal-frontend
      template:
        metadata:
          labels:
            app: portal-frontend
        spec:
          serviceAccountName: portal-frontend
          containers:
            - image: gcr.io/solo-public/docs/portal-frontend:latest
              imagePullPolicy: Always
              name: portal-frontend
              args: ["--host", "0.0.0.0"]
              ports:
                - containerPort: 4000
              env:
                - name: VITE_PORTAL_SERVER_URL
                  value: http://developer.example.com/v1 # The URL that the backend Portal is exposed on via a RouteTable.
    EOF
      
  2. Expose the frontend app securely through the ingress gateway. The following example uses the host portal.example.com. It refers to the virtual gateway that you created when you configured the developer portal. For more examples such as HTTPS/TLS, see Configure gateway listeners.

      kubectl apply -f- <<EOF
    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: portal-frontend
      namespace: gloo-mesh-gateways
      labels:
        app: portal-frontend
    spec:
      hosts:
        - "portal.example.com"
      virtualGateways:
        - name: istio-ingressgateway-portal
          namespace: gloo-mesh-gateways
      http:
        - name: portal-frontend
          forwardTo:
            destinations:
              - port:
                  number: 4000
                ref:
                  name: portal-frontend
                  namespace: gloo-mesh-addons
                  cluster: $CLUSTER_NAME
          labels:
            route: portal-api
          matchers:
            - uri:
                prefix: /
    EOF
      

You are almost there! You have all the pieces for a basic developer portal set up. Now, you can check out the portal as an end user.

Explore the portal

Explore how your end users can use the developer portal to discover, access, and use your API products. Because you set the portal visibility to public and did not enforce authentication, you can review all the pages that are available in the web UI.

  1. To have the routing for the frontend portal work on your local machine, update your /etc/hosts to map the IP address of the Kubernetes Gateway to the routes that you created for your APIs, portal backend, and portal frontend. If you are using an actual domain that you own to serve the frontend app, you can skip this step.

      sudo nano /etc/hosts
      

    Replace 18.xxx.xxx.xx with the value of the IP address of your gateway.

      18.xxx.xxx.xx portal.example.com
    18.xxx.xxx.xx developer.example.com
    18.xxx.xxx.xx api.petstore.com
    18.xxx.xxx.xx api.tracks.com
      
  2. In your browser, open the web UI of the frontend portal app, http://portal.example.com:8080/.

      open http://portal.example.com:8080/
      
    Figure: Welcome page of the developer portal.
    Figure: Welcome page of the developer portal.
  3. From the menu bar, click the APIs tab. You find the two ApiProducts that you created in this tutorial. Note that the portal metadata that you configured is shown for the description, version, tags, and other information.

    Figure: API products in the developer portal.
    Figure: API products in the developer portal.
  4. Click the Pet Store REST API. You can review the OpenAPI spec for each route in the ApiProduct, for pet, store, and user. To get a JSON file of the OpenAPI spec from either view, click Download.

    Figure: Redocly view of the OpenAPI spec for the Petstore ApiProduct.
    Figure: Redocly view of the OpenAPI spec for the Petstore ApiProduct.
  5. Click Swagger View to toggle the view from Redocly to Swagger.

    Figure: Swagger view of the OpenAPI spec for the Petstore ApiProduct.
    Figure: Swagger view of the OpenAPI spec for the Petstore ApiProduct.

Good job! You set up and explored the developer portal.

Next steps

So far, you have a basic portal backend and frontend set up. However, the portal is not secured. Anyone with the link can access the portal. Continue with the following tutorials to add more security to your portal: