Start typing to search...

Add a backend service

As you develop and extend apps on Koji, you will likely encounter situations where it would be useful to put some of your code into a service. In general, a service is a software component that provides functionality to clients. It’s usually set up to be accessible via public or internal ports. The client software can then access the functionality provided by the service by using HTTP calls.

In this tutorial, you will add a backend service to an existing app and confirm that it’s working.

Note
In Koji apps, the frontend also runs as a service. This allows your apps to be run inside other applications and even inside other Koji apps.

Prerequisites

  • Familiarity with web development. React and ES6 basics are a plus.

  • Familiarity with the Koji editor and customization process. For an overview, go through the Koji starter tutorial.

Setting up the app

To add and test a backend service, you’ll start by forking an app that already has a frontend.

  1. Open the following scaffold.

  2. Click Fork. The Project Details page opens.

  3. Click Open in Code Editor. The app opens in the Koji editor.

Adding the backend project

The backend will be an npm project, just like the frontend. You will use these files to get started:

  • package.json – Defines the packages and commands.

  • .babelrc – Helps with the transpiling.

  • server.js – Runs a simple Express server that the frontend will be able to call.

You will configure these files and install the required packages for the backend service.

  1. On the left side of the editor, click the plus (+) next to the frontend folder in the source code, click New File, and then enter the following path:

    /backend/package.json

    This path creates a backend folder and a package.json file.

  2. Paste the following code into package.json, and then save the file.

    {
      "name": "backend",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "compile": "rm -rf dist && ./node_modules/.bin/babel ./src -d dist --ignore \"node_modules/**/*.js\"",
        "start": "NODE_ENV=production node dist/server.js",
        "start-dev": "nodemon --exec babel-node src/server.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "@withkoji/core": "^1.7.3",
        "body-parser": "^1.19.0",
        "cors": "^2.8.5",
        "dotenv": "^8.2.0",
        "express": "^4.17.1",
        "uuid": "^8.3.0"
      },
      "devDependencies": {
        "@babel/cli": "^7.11.6",
        "@babel/core": "^7.11.6",
        "@babel/node": "^7.10.5",
        "@babel/plugin-proposal-object-rest-spread": "^7.11.0",
        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
        "@babel/preset-env": "^7.11.5",
        "babel-plugin-inline-json-import": "^0.3.2",
        "eslint": "^7.8.1",
        "eslint-config-airbnb-base": "^14.2.0",
        "eslint-plugin-import": "^2.22.0",
        "nodemon": "^2.0.4"
      }
    }
  3. To open a new terminal, click New tab at the bottom of the editor.

    You should see a new command line interface, open to /usr/src/app.

  4. Go to the backend folder.

    cd backend
  5. Install the packages.

    npm install

    You should see the installation progress messages. After installation is complete, you should be returned to the command line. The required packages are now installed.

  6. To close the terminal, run the following command.

    exit
  7. To collapse the terminal pane, click the down arrow in the upper right.

  8. On the left side of the editor, click the plus (+) next to any folder in the source code, click New File, and then enter the following path.

    /backend/.babelrc

    This path creates a .babelrc file in the backend folder.

  9. Paste the following code into .babelrc, and then save the file.

    {
      "presets": ["@babel/preset-env"],
      "plugins": ["@babel/plugin-proposal-object-rest-spread"]
    }

    These Babel configurations add syntax that will help your project build and compile correctly.

  10. On the left side of the editor, click the plus (+) next to any folder in the source code, click New File, and then enter the following path:

    /backend/src/server.js

    This path creates a src folder with a server.js file inside it. You will use this file to run the backend server.

  11. Paste the following code into server.js, and then save the file.

    import '@babel/core';
    import express from 'express';
    import * as fs from 'fs';
    import bodyParser from 'body-parser';
    import cors from 'cors';
    import { KojiBackend } from '@withkoji/core';
    
    // Create server
    const app = express();
    
    // Specifically enable CORS for pre-flight options requests
    app.options('*', cors())
    
    // Enable body parsers for reading POST data. We set up this app to
    // accept JSON bodies and x-www-form-urlencoded bodies. If you wanted to
    // process other request types, like form-data or graphql, you would need
    // to include the appropriate parser middlewares here.
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      limit: '2mb',
      extended: true,
    }));
    
    // CORS allows these API routes to be requested directly by browsers
    app.use(cors());
    
    // Disable caching
    app.use((req, res, next) => {
      res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
      res.header('Expires', '-1');
      res.header('Pragma', 'no-cache');
      next();
    });
    
    app.get('/', async (req, res) => {
        res.status(200).json({
            text: 'Hello, world!',
        });
    });
    
    // Start server
    app.listen(process.env.PORT || 3333, null, async err => {
        if (err) {
            console.log(err.message);
        }
        console.log('[koji] backend started');
    });

    This code will start a basic Express server, which will respond to a GET with JSON that you can verify on the frontend.

Accessing internal Koji variables

There are several values your app will need to get from the Koji platform.

  • The URL for the frontend.

  • The URL for the backend.

  • The project ID.

Koji stores these values in internal variables. To make them accessible to your app, you need to map them to environment variables, and then add them to the configuration.

  1. Open the frontend/.env file, update the contents as follows, and then save the file.

    REACT_APP_FRONTEND_URL=$KOJI_SERVICE_URL_frontend
    REACT_APP_BACKEND_URL=$KOJI_SERVICE_URL_backend
    REACT_APP_PROJECT_ID=$KOJI_PROJECT_ID
  2. Open the frontend/src/index.js file, update the config call as follows, and then save the file.

    ....
    Koji.config(kojiConfig, {
      projectId: process.env.REACT_APP_PROJECT_ID,
      services: {
        backend: process.env.REACT_APP_BACKEND_URL,
        frontend: process.env.REACT_APP_FRONTEND_URL
      },
    });
    ....

Letting Koji know about the backend service

You have now added a backend project, but the Koji editor and app builder still don’t know it exists. You must update the koji.json file to let Koji know about the new service. koji.json is located in the root directory.

  1. Open the koji.json file, and add the deployment and development instructions for the backend service to the deploy and develop keys.

    "develop": {
      "frontend": {
        "path": "frontend",
        "port": 8080,
        "startCommand": "npm start"
      },
      "backend": {
          "path": "backend",
          "port": 3333,
          "startCommand": "npm run start-dev"
      }
    },
    "deploy": {
      "frontend": {
        "output": "frontend/build",
        "type": "static",
        "commands": [
          "cd frontend",
          "npm install",
          "export NODE_ENV=production && npm run build"
        ]
      },
      "backend": {
        "output": "backend",
        "type": "dynamic",
        "commands": [
          "cd backend",
          "npm install",
          "export NODE_ENV=production && npm run compile"
        ]
      }
    },

    This file lets the Koji editor know which services to run, so you must add entries for the backend service.

  2. On the left side of the editor, go to Advanced  Remote environment and click Force restart project.

    If everything went well, you should be able to reconnect to the editor and see that a backend terminal has been added at the bottom.

    If you see [koji] backend started, your backend service is now running in your local editor environment.

Testing the backend service

To test that everything is working correctly, you can modify your frontend code to get a value from the backend service and display it automatically.

  1. Go to the frontend terminal, and press Ctrl+C to stop the frontend server.

  2. Install the styled-components package, which the new code will use.

    npm install styled-components
  3. Restart the frontend server.

    npm start
  4. Open the /frontend/src/App.js file and replace the contents with the following code.

    import React, { useEffect, useState } from 'react';
    import styled, { keyframes } from 'styled-components';
    import Koji from '@withkoji/core';
    
    const Container = styled.div`
        background-color: #395B88;
        min-height: 100vh;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        font-size: calc(10px + 2vmin);
        color: #FCFCFC;
        text-align: center;
    `;
    
    const AppLogoSpin = keyframes`
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    `;
    
    const Icon = styled.div`
        animation: ${AppLogoSpin} infinite 20s linear;
        height: 50vmin;
        width: 50vmin;
        pointer-events: none;
        background-image: url("https://images.koji-cdn.com/d9c6b38e-08c4-4faf-ae8e-01082c41a0fb/3f83q-9634d620e97345a6b250ca2feb7e5a2e.png");
        background-size: contain;
        background-repeat: no-repeat;
        margin-bottom: 16px;
    `;
    
    function App() {
      const [text, setText] = useState('');
    
      useEffect(() => {
        fetch(Koji.services.backend)
          .then((response) => response.json())
          .then((jsonResponse) => {
            setText(jsonResponse.text);
          });
      }, []);
    
      return (
        <Container>
          <p>{text}</p>
          <Icon />
        </Container>
      );
    }
    
    export default App;

    This code fetches the value for text from your backend service.

  5. Open the live preview and refresh it.

    If everything worked, you should see Hello, world! displayed above the React logo.

  6. Publish your app, and confirm that it’s working as expected.

Tip

If you’re wondering how these services work behind the scenes, here’s a brief explanation:

  1. The develop and deploy sections in koji.json contain service keys, such as frontend.

  2. For each of these keys, the container that handles your editor or published deployment will automatically generate a URL.

  3. That URL is saved as a variable inside Koji, which is based on the key. The variable name is $KOJI_SERVICE_URL_backend.

  4. In frontend/.env, you defined an environment variable that points to this internal Koji variable, so that your app is able to retrieve the value.

Wrapping up

Hopefully, this tutorial has given you a better understanding of how to add a service.

Tip
When adding backend services to your apps, keep in mind that requests to Koji backends are rate limited to protect against bursts of traffic. Users who send too many requests in quick succession might see error messages with a status code of 429. To gracefully handle rate limiting in your apps, you can watch for this status code and build in a retry mechanism.