/backend/package.json
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. |
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.
To add and test a backend service, you’ll start by forking an app that already has a frontend.
Open the following scaffold.
Click Fork. The Project Details page opens.
Click Open in Code Editor. The app opens in the Koji editor.
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.
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.
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"
}
}
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
.
Go to the backend
folder.
cd backend
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.
To close the terminal, run the following command.
exit
To collapse the terminal pane, click the down arrow in the upper right.
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.
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.
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.
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.
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.
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
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
},
});
....
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.
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.
On the left side of the editor, go to
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.
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.
Go to the frontend
terminal, and press Ctrl+C to stop the frontend server.
Install the styled-components
package, which the new code will use.
npm install styled-components
Restart the frontend server.
npm start
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.
Open the live preview and refresh it.
If everything worked, you should see Hello, world!
displayed above the React logo.
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:
|
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.
|