Build a Mobile App with React Native and Spring Boot

avatar-matt_raible.jpg Matt Raible

React Native is a framework for building mobile applications with React. React allows you to use a declarative style of programming to describe how your UI should look. It uses embedded HTML (called JSX) to render buttons, lists, scrollable views, and many other components.

I’m a seasoned Java and JavaScript developer that loves Spring and TypeScript. Some might call me a Java hipster because I like JavaScript. In this post, I’m going to show you how to build a Spring Boot API that talks to a PostgreSQL database. You’ll use Elasticsearch to make your data searchable. You’ll also learn how to deploy it to Cloud Foundry, and Google Cloud Platform using Kubernetes.

The really cool part is you’ll see how to build a mobile app with React Native. React Native allows you to build mobile apps with the web technologies you know and love: React and JavaScript! I’ll show you how to test it on device emulators and deploy it to your phone. Giddyup!

Create a Spring Boot App

In my recent developer life, I built an app to help me track and monitor my health. I came up with the idea while writing the JHipster Mini-Book. I was inspired by Spring Boot’s Actuator, which helps you monitor the health of your Spring Boot app. The app is called 21-Points Health and you can find its source code on GitHub.

21-Points Health uses a 21-point system to see how healthy you are being each week. Its rules are simple: you can earn up to three points per day for the following reasons:

  1. If you eat healthy, you get a point. Otherwise, zero.

  2. If you exercise, you get a point.

  3. If you don’t drink alcohol, you get a point.

I’m going to cheat a bit in this tutorial. Rather than writing every component line-by-line, I’m going to generate the API and the app using JHipster and Ignite JHipster.

What is JHipster?

I’m glad you asked! It’s an Apache-licensed open source project that allows you to generate Spring Boot APIs, as well as Angular or React UIs. It includes support for generating CRUD screens and adding all the necessary plumbing. It even generates microservice architectures!

Ignite JHipster is a complementary feature of JHipster. It’s a blueprint template for the Ignite CLI project. Ignite CLI is open source and MIT licensed, produced by the good folks at Infinite Red. Ignite CLI allows you to generate React Native apps in seconds with a number of components pre-integrated. I was blown away the first time I saw a demo of it from Gant Laborde.

To get things moving quickly, I ran jhipster export-jdl to export an entity definition from 21-Points Health. After exporting the entity definitions, I used JDL-Studio to create an application definition for my project. Then I clicked the download icon to save the file to my hard drive.

JDL-Studio

The code you see below is called JDL, or JHipster Domain Language. It was initially designed for JHipster to allow multiple entities and specifying all their attributes, relationships, and pagination features. It’s recently been enhanced to allow generating whole apps from a single file! 💥

application {
  config {
    applicationType monolith,
    baseName HealthPoints
    packageName com.okta.developer,
    authenticationType oauth2,
    prodDatabaseType postgresql,
    buildTool gradle,
    searchEngine elasticsearch,
    testFrameworks [protractor],
    clientFramework react,
    useSass true,
    enableTranslation true,
    nativeLanguage en,
    languages [en, es]
  }
  entities Points, BloodPressure, Weight, Preferences
}

// JDL definition for application 'TwentyOnePoints' generated with command 'jhipster export-jdl'

entity BloodPressure {
  timestamp ZonedDateTime required,
  systolic Integer required,
  diastolic Integer required
}
entity Weight {
  timestamp ZonedDateTime required,
  weight Double required
}
entity Points {
  date LocalDate required,
  exercise Integer,
  meals Integer,
  alcohol Integer,
  notes String maxlength(140)
}
entity Preferences {
  weeklyGoal Integer required min(10) max(21),
  weightUnits Units required
}

enum Units {
  KG,
  LB
}

relationship OneToOne {
  Preferences{user(login)} to User
}
relationship ManyToOne {
  BloodPressure{user(login)} to User,
  Weight{user(login)} to User,
  Points{user(login)} to User
}

paginate BloodPressure, Weight with infinite-scroll
paginate Points with pagination

Create a new directory, with a jhipster-api directory inside it.

mkdir -p react-native-spring-boot/jhipster-api

Copy the JDL above into an app.jh file inside the react-native-spring-boot directory. Install JHipster using npm.

npm i -g generator-jhipster@5.4.2

Navigate to the jhipster-api directory in a terminal window. Run the command below to generate an app with a plethora of useful features out-of-the-box.

jhipster import-jdl ../app.jh

Run Your Spring Boot App

This app has a number of technologies and features specified as part of its application configuration, including OIDC auth, PostgreSQL, Gradle, Elasticsearch, Protractor tests, React, and Sass. Not only that, it even has test coverage for most of its code!

To make sure your app is functional, start a few Docker containers for Elasticsearch, Keycloak, PostgreSQL, and Sonar. The commands below should be run from the jhipster-api directory.

docker-compose -f src/main/docker/elasticsearch.yml up -d
docker-compose -f src/main/docker/keycloak.yml up -d
docker-compose -f src/main/docker/postgresql.yml up -d
docker-compose -f src/main/docker/sonar.yml up -d

The containers might take a bit to download, so you might want to grab a coffee, or a glass of water.

While you’re waiting, you can also commit your project to Git. If you have Git installed, JHipster will run git init in your jhipster-api directory. Since you’re putting your Spring Boot app and React Native app in the same repository, remove .git from jhipster-api and initialize Git in the parent directory.

rm -rf jhipster-api/.git
git init
git add .
git commit -m "Generate Spring Boot API"

Ensure Test Coverage with Sonar

JHipster generates apps with high code quality. Code quality is analyzed using SonarCloud, which is automatically configured by JHipster. The "code quality" metric is determined by the percentage of code that is covered by tests.

Once all the Docker containers have finished starting, run the following command to prove code quality is 👍 (from the jhipster-api directory).

./gradlew -Pprod clean test sonarqube
If you don’t commit your project to Git, the sonarqube task might fail.

Once this process completes, an analysis of your project will be available on the Sonar dashboard at http://127.0.0.1:9001. Check it - you have a triple-A-rated app! Not bad, eh?

Sonar AAA

Create a React Native App for Your Spring Boot API

You can build a React Native app for your Spring Boot API using Ignite JHipster, created by Jon Ruddell. Jon is one of the most prolific JHipster contributors. ❤️

Ignite JHipster

Install Ignite CLI:

npm i -g ignite-cli@2.1.2 ignite-jhipster@1.12.1

Make sure you’re in the react-native-spring-boot directory, then generate a React Native app.

ignite new HealthPoints -b ignite-jhipster

When prompted for the path to your JHipster project, enter jhipster-api.

When the project is finished generating, rename HealthPoints to react-native-app, then committed it to Git.

mv HealthPoints react-native-app
rm -rf react-native-app/.git
git add .
git commit -m "Add React Native app"

You might notice that two new files were added to your API project.

create mode 100644 jhipster-api/src/main/java/com/okta/developer/config/ResourceServerConfiguration.java
create mode 100644 jhipster-api/src/main/java/com/okta/developer/web/rest/AuthInfoResource.java

These classes configure a resource server for your project (so you can pass in an Authorization header with an access token) and expose the OIDC issuer and client ID via a REST endpoint.

Modify React Native App for OAuth 2.0 / OIDC Login

You will need to make some changes to your React Native app so OIDC login works. I’ve summarized them below.

Update Files for iOS

If you’d like to run your app on iOS, you’ll need to modify react-native-app/ios/HealthPoints/AppDelegate.m to add an openURL() method and an import at the top.

#import <React/RCTLinkingManager.h>

Then add the method before the @end at the bottom of the file.

- (BOOL)application:(UIApplication *)application
           openURL:(NSURL *)url
           options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
 return [RCTLinkingManager application:application openURL:url options:options];
}

You’ll also need to configure your iOS URL scheme. Run open ios/HealthPoints.xcodeproj to open the project in Xcode. Navigate to Project > Info > URL Types and specify healthpoints like in the screenshot below.

Xcode URL Scheme

You can also modify ios/HealthPoints/Info.plist if you’d rather not use Xcode.

        <key>CFBundleSignature</key>
        <string>????</string>
+       <key>CFBundleURLTypes</key>
+       <array>
+               <dict>
+                       <key>CFBundleTypeRole</key>
+                       <string>Editor</string>
+                       <key>CFBundleURLName</key>
+                       <string>healthpoints</string>
+                       <key>CFBundleURLSchemes</key>
+                       <array>
+                               <string>healthpoints</string>
+                       </array>
+               </dict>
+       </array>
        <key>CFBundleVersion</key>

Update Files for Android

To make the Android side of things aware of your URL scheme, add it to android/app/src/main/AndroidManifest.xml. The following XML should go just after the existing <intent-filter>.

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    <data android:scheme="healthpoints" />
</intent-filter>

You’ll also need to add an android:launchMode attribute to the main <application> element. This is needed for Android to receive the deep link response in the existing MainActivity instead of a new one.

    <application
      android:name=".MainApplication"
      android:launchMode="singleTask"

Update Keycloak’s Redirect URI

You will also need to update Keycloak to know your app’s URL scheme because it’s used as a redirect URI. Open http://localhost:9080/auth/admin in your browser and log in with admin/admin. Navigate to Clients > web_app and add healthpoints://authorize as a valid redirect URI.

Valid Redirect URIs

Run Your React Native App on iOS

To run your React Native app, you’ll need to start your Spring Boot app first. Navigate to the jhipster-api directory and run ./gradlew. In another terminal window, navigate to react-native-app and run react-native run-ios.

If you get an error Print: Entry, ":CFBundleIdentifier", Does Not Exist, run rm -rf ~/.rncache and try again.

Verify you can log in by clicking the hamburger menu in the top left corner, then Login. Use "admin" for the username and password.

Ignite JHipster with Keycloak
To enable live-reloading of your code in iOS Simulator, first click on the emulator, then press +R.

Run Your React Native App on Android

To run your app on an Android emulator, run react-native run-android. If you don’t have a phone plugged in or an Android Virtual Device (AVD) running, you’ll see an error:

Could not install the app on the device, read the error above for details.

To fix this, open Android Studio, choose open existing project, and select the android directory in your project. If you’re prompted to "Install Build Tools and sync project," do it.

To create a new AVD, navigate to Tools > Android > AVD Manager. Create a new Virtual Device and click Play. I chose a Pixel 2 as you can see from my settings below.

AVD Pixel 2

To make Keycloak and your API work with Android in an emulator, you’ll have to change all localhost links to your computer’s IP address (e.g., 192.168.0.2).

This means you’ll need to update src/main/resources/config/application.yml in the JHipster app to the following.

security:
    oauth2:
        client:
            access-token-uri: http://{yourIPAddress}:9080/auth/realms/jhipster/protocol/openid-connect/token
            user-authorization-uri: http://{yourIPAddress}:9080/auth/realms/jhipster/protocol/openid-connect/auth
            client-id: web_app
            client-secret: web_app
            scope: openid profile email
        resource:
            user-info-uri: http://{yourIPAddress}:9080/auth/realms/jhipster/protocol/openid-connect/userinfo

You’ll also need to update apiUrl in your React Native app’s App/Config/AppConfig.js.

export default {
  apiUrl: 'http://{yourIPAddress}:8080/',
  appUrlScheme: 'healthpoints'
}

Run react-native run-android again. You should be able to log in just like you did on iOS.

To enable live-reloading of code on Android, first click on the emulator, then press Ctrl+M (+M on MacOS) or shake the Android device which has the running app. Then select the Enable Live Reload option from the popup.

For the rest of this tutorial, I’m going to show all the examples on iOS, but you should be able to use Android if you prefer.

Generate CRUD Pages in React Native App

To generate pages for managing entities in your Spring Boot API, run the following command in the react-native-app directory.

ignite generate import-jdl ../app.jh

Run react-native run-ios, log in, and click the Entities menu item. You should see a screen like the one below.

Ignite JHipster Entities Screen

Click on Points and you should be able to add points.

Create Points Screen

Tweak React Native Points Edit Screen to use Toggles

The goal of my 21-Points Health app is to count the total number of health points you get in a week, with the max being 21. For this reason, I think it’s a good idea to change the integer inputs on exercise, meals, and alcohol to be toggles instead of raw integers. If the user toggles it on, the app should store the value as "1". If they toggle it off, it should record "0".

To make this change to the React Native app, open App/Containers/PointEntityEditScreen.js in your favorite editor. Change the formModel to use t.Boolean for exercise, meals, and alcohol.

formModel: t.struct({
  id: t.maybe(t.Number),
  date: t.Date,
  exercise: t.maybe(t.Boolean),
  meals: t.maybe(t.Boolean),
  alcohol: t.maybe(t.Boolean),
  notes: t.maybe(t.String),
  userId: this.getUsers()
}),

Then change the entityToFormValue() and formValueToEntity() methods to save 1 or 0, depending on the user’s selection.

entityToFormValue = (value) => {
  if (!value) {
    return {}
  }
  return {
    id: value.id || null,
    date: value.date || null,
    exercise: value.exercise === 1 ? true : false,
    meals: value.meals === 1 ? true : false,
    alcohol: value.alcohol === 1 ? true : false,
    notes: value.notes || null,
    userId: (value.user && value.user.id) ? value.user.id : null
  }
}
formValueToEntity = (value) => {
  return {
    id: value.id || null,
    date: value.date || null,
    exercise: (value.exercise) ? 1 : 0,
    meals: (value.meals) ? 1 : 0,
    alcohol: (value.alcohol) ? 1 : 0,
    notes: value.notes || null,
    user: value.userId ? { id: value.userId } : null
  }
}

While you’re at it, you can change the default Points entity to have today’s date and true for every point by default. You can make this happen by modifying componentWillMount() and changing the formValue.

componentWillMount () {
  if (this.props.entityId) {
    this.props.getPoint(this.props.entityId)
  } else {
    this.setState({formValue: {date: new Date(), exercise: true, meals: true, alcohol: true}})
  }
  this.props.getAllUsers()
}

Refresh your app in Simulator using +M. When you create new points, you should see your new defaults.

Create Points with defaults

Tweak React App’s Points to use Checkboxes

Since your JHipster app has a React UI as well, it makes sense to change the points input/edit screen to use a similar mechanism: checkboxes. Open jhipster-api/src/main/webapp/…​/points-update.tsx and replace the TSX (the T is for TypeScript) for the three fields with the following. You might notice the trueValue and falseValue attributes handle converting checked to true and vise versa.

jhipster-api/src/main/webapp/app/entities/points/points-update.tsx
<AvGroup check>
  <AvInput id="points-exercise" type="checkbox" className="form-control"
    name="exercise" trueValue={1} falseValue={0} /> // (1)
  <Label check id="exerciseLabel" for="exercise">
    <Translate contentKey="healthPointsApp.points.exercise">Exercise</Translate>
  </Label>
</AvGroup>
<AvGroup check>
  <AvInput id="points-meals" type="checkbox" className="form-control"
    name="meals" trueValue={1} falseValue={0} />
  <Label check id="mealsLabel" for="meals">
    <Translate contentKey="healthPointsApp.points.meals">Meals</Translate>
  </Label>
</AvGroup>
<AvGroup check>
  <AvInput id="points-alcohol" type="checkbox" className="form-control"
    name="alcohol" trueValue={1} falseValue={0} />
  <Label check id="alcoholLabel" for="alcohol">
    <Translate contentKey="healthPointsApp.points.alcohol">Alcohol</Translate>
  </Label>
</AvGroup>

In the jhipster-api directory, run npm start (or yarn start) and verify your changes exist. The screenshot below shows what it looks like when editing a record entered by the React Native app.

checkboxes in React app

Use Okta’s API for Identity

Switching from Keycload to Okta for identity in a JHipster app is suuuper easy thanks to Spring Boot and Spring Security. First, you’ll need an Okta developer account. If you don’t have one already, you can signup at developer.okta.com/signup. Okta is an OIDC provider like Keycloak, but it’s always on, so you don’t have to manage it.

Okta Developer Signup

Log in to your Okta Developer account and navigate to Applications > Add Application. Click Web and click Next. Give the app a name you’ll remember, and specify http://localhost:8080/login and healthpoints://authorize as Login redirect URIs. Click Done, then edit it again to select "Implicit (Hybrid)" + allow ID and access tokens. Note the client ID and secret, you’ll need to copy/paste them into a file in a minute.

Create a ROLE_ADMIN and ROLE_USER group (Users > Groups > Add Group) and add users to them. I recommend adding the account you signed up with to ROLE_ADMIN and creating a new user (Users > Add Person) to add to ROLE_USER.

Navigate to API > Authorization Servers and click the one named default to edit it. Click the Claims tab and Add Claim. Name it "roles", and include it in the ID Token. Set the value type to "Groups" and set the filter to be a Regex of .*. Click Create to complete the process.

Create a file on your hard drive called ~/.okta.env and specify the settings for your app in it.

#!/bin/bash
export SECURITY_OAUTH2_CLIENT_ACCESS_TOKEN_URI="https://{yourOktaDomain}/oauth2/default/v1/token"
export SECURITY_OAUTH2_CLIENT_USER_AUTHORIZATION_URI="https://{yourOktaDomain}/oauth2/default/v1/authorize"
export SECURITY_OAUTH2_RESOURCE_USER_INFO_URI="https://{yourOktaDomain}/oauth2/default/v1/userinfo"
export SECURITY_OAUTH2_CLIENT_CLIENT_ID="{yourClientId}"
export SECURITY_OAUTH2_CLIENT_CLIENT_SECRET="{yourClientSecret}"
Make sure your *URI variables do not have -admin in them. This is a common mistake.

In the terminal where your Spring Boot app is running, kill the process, run source ~/.okta.env and run ./gradlew again. You should be able to log in at http://localhost:8080 in your browser, and in your React Native app (after you refresh or restart it).

Okta Login in React Native

Debugging React Native Apps

If you have issues, or just want to see what API calls are being made, you can use Reactotron. Reactotron is a desktop app for inspecting your React and React Native applications. It should work with iOS without any changes. For Android, you’ll need to run adb reverse tcp:9090 tcp:9090 after your AVD is running.

Once it’s running, you can see API calls being made, as well as log messages.

Reactotron

If you’d like to log your own messages to Reactotron, you can use console.tron.log('debug message').

Packaging Your React Native App for Production

The last thing I’d like to show you to deploy your app to production. Since there are many steps to getting your React Native app onto a physical device, I’ll defer to React Native’s Running on Device documentation. It should be as simple as plugging in your device via USB, configuring code signing, and building/running your app. You’ll also need to configure the URL of where your API is located.

You know what’s awesome about Spring Boot? There’s a bunch of cloud providers that support it! If a platform supports Spring Boot, you should be able to run a JHipster app on it!

Follow the instructions below to deploy your API to Pivotal’s Cloud Foundry and Google Cloud Platform using Kubernetes. Both Cloud Foundry and Kubernetes have multiple providers, so these instructions should work even if you’re not using Pivotal or Google.

Deploy Your Spring Boot API to Cloud Foundry

JHipster has a Cloud Foundry sub-generator that makes it simple to deploy to Cloud Foundry. It only requires you run one command. However, you have Elasticsearch configured in your API and the sub-generator doesn’t support automatically provisioning an Elasticsearch instance for you. To workaround this limitation, modify jhipster-api/src/main/resources/config/application-prod.yml and find the following configuration for Spring Data Jest:

data:
    jest:
        uri: http://localhost:9200

Replace it with the following, which will cause Elasticsearch to run in embedded mode.

data:
    elasticsearch:
        properties:
            path:
                home: /tmp/elasticsearch

You’ll also need to remove a couple of properties, due to an issue I discovered in JHipster.

@@ -30,15 +30,12 @@ spring:
         url: jdbc:postgresql://localhost:5432/HealthPoints
         username: HealthPoints
         password:
-        hikari:
-            auto-commit: false
     jpa:
         database-platform: io.github.jhipster.domain.util.FixedPostgreSQL82Dialect
         database: POSTGRESQL
         show-sql: false
         properties:
             hibernate.id.new_generator_mappings: true
-            hibernate.connection.provider_disables_autocommit: true
             hibernate.cache.use_second_level_cache: true
             hibernate.cache.use_query_cache: false
             hibernate.generate_statistics: false

To deploy everything on Cloud Foundry with Pivotal Web Services, you’ll need to create an account, download/install the Cloud Foundry CLI, and sign-in (using cf login -a api.run.pivotal.io).

You may receive a warning after logging in No space targeted, use 'cf target -s SPACE'. If you do, log in to https://run.pivotal.io in your browser, create a space, then run the command as recommended.

Then run jhipster cloudfoundry in the jhipster-api directory. You can see the values I chose when prompted below.

CloudFoundry configuration is starting
? Name to deploy as? HealthPoints
? Which profile would you like to use? prod
? What is the name of your database service? elephantsql
? What is the name of your database plan? turtle

When prompted to overwrite build.gradle, type a.

The first time I ran jhipster cloudfoundry, it didn’t work. Running it a second time succeeded.
source ~/.okta.env
export CF_APP_NAME=healthpoints
cf set-env $CF_APP_NAME FORCE_HTTPS true
cf set-env $CF_APP_NAME SECURITY_OAUTH2_CLIENT_ACCESS_TOKEN_URI "$SECURITY_OAUTH2_CLIENT_ACCESS_TOKEN_URI"
cf set-env $CF_APP_NAME SECURITY_OAUTH2_CLIENT_USER_AUTHORIZATION_URI "$SECURITY_OAUTH2_CLIENT_USER_AUTHORIZATION_URI"
cf set-env $CF_APP_NAME SECURITY_OAUTH2_RESOURCE_USER_INFO_URI "$SECURITY_OAUTH2_RESOURCE_USER_INFO_URI"
cf set-env $CF_APP_NAME SECURITY_OAUTH2_CLIENT_CLIENT_ID "$SECURITY_OAUTH2_CLIENT_CLIENT_ID"
cf set-env $CF_APP_NAME SECURITY_OAUTH2_CLIENT_CLIENT_SECRET "$SECURITY_OAUTH2_CLIENT_CLIENT_SECRET"
cf restage healthpoints

After overriding the default OIDC settings for Spring Security, you’ll need to add https://healthpoints.cfapps.io/login as a redirect URI in your Okta OIDC application.

Then…​ you’ll be able to authenticate. Voila! 😃

JHipster API on Cloud Foundry

Modify your React Native application’s apiUrl (in App/Config/AppConfig.js) to be https://healthpoints.cfapps.io/ and deploy it to your phone. Hint: use the "running on device" docs I mentioned earlier.

export default {
  apiUrl: 'https://healthpoints.cfapps.io/',
  appUrlScheme: 'healthpoints'
}

I used Xcode on my Mac (open react-native-app/ios/HealthPoints.xcodeproj) and deployed it to an iPhone X.

When I encountered build issues in Xcode, I ran rm -rf ~/.rncache and it fixed them. I also used a bit of rm -rf node_modules && yarn.

Below are screenshots that show it worked!

Login and Entities on iPhone X

Deploy Your Spring Boot API to Google Cloud Platform using Kubernetes

JHipster also supports deploying your app to the 🔥 hottest thing in production: Kubernetes!

To try it out, create a k8s directory alongside your jhipster-api directory. Then run jhipster kubernetes in it. When prompted, specify the following answers:

  • Type of application: Monolithic application

  • Root directory: ../

  • Which applications: jhipster-api

  • Setup monitoring: No

  • Kubernetes namespace: default

  • Docker repository name: <your Docker Hub username>

  • Docker command: docker push

  • Kubernetes service type: LoadBalancer

A number of commands will be printed out that you need to run. Run the following in the jhipster-api directory.

docker login
export USERNAME=<your username>
./gradlew bootWar -Pprod jibDockerBuild
docker image tag healthpoints $USERNAME/healthpoints
docker push $USERNAME/healthpoints

Google Cloud Platform (a.k.a., GCP) is a PaaS (Platform As A Service) that is built on Google’s core infrastructure. It’s naturally a good provider for Kubernetes in the cloud. Complete the steps below to deploy your JHipster API to Google Cloud.

  1. Create a Google Cloud project at console.cloud.google.com.

  2. Navigate to https://console.cloud.google.com/kubernetes/list to initialize the Kubernetes Engine for your project. If your project is not auto-selected, select it (in the nav bar) to start GKE initialization in GCP.

  3. Install Google Cloud SDK, log in, and set the project using:

    gcloud auth login
    gcloud config set project <project-name>
  4. Create a cluster:

    gcloud components install kubectl
    gcloud container clusters create <cluster-name> --machine-type n1-standard-2 --scopes cloud-platform --zone us-west1-a

    To see a list of possible zones, run gcloud compute zones list.

  5. Set the environment variables for your Spring Boot application to use Okta for Identity. You can do this by modifying k8s/healthpoints/healthpoints-deployment.yml, adding to the env list, and specifying your SECURITY_OAUTH2_* values for your Okta OIDC app. In other words, add the name/value pairs just after JAVA_OPTS.

    containers:
    - name: healthpoints-app
      image: mraible/healthpoints
      env:
      - name: SPRING_PROFILES_ACTIVE
        value: prod
      - name: SPRING_DATASOURCE_URL
        value: jdbc:postgresql://healthpoints-postgresql.default.svc.cluster.local:5432/HealthPoints
      - name: SPRING_DATA_JEST_URI
        value: http://healthpoints-elasticsearch.default.svc.cluster.local:9200
      - name: JAVA_OPTS
        value: " -Xmx256m -Xms256m"
      - name: SECURITY_OAUTH2_CLIENT_ACCESS_TOKEN_URI
        value: "https://{yourOktaDomain}/oauth2/default/v1/token"
      - name: SECURITY_OAUTH2_CLIENT_USER_AUTHORIZATION_URI
        value: "https://{yourOktaDomain}/oauth2/default/v1/authorize"
      - name: SECURITY_OAUTH2_RESOURCE_USER_INFO_URI
        value: "https://{yourOktaDomain}/oauth2/default/v1/userinfo"
      - name: SECURITY_OAUTH2_CLIENT_CLIENT_ID
        value: "{yourClientId}"
      - name: SECURITY_OAUTH2_CLIENT_CLIENT_SECRET
        value: "{yourClientSecret}"
  6. Run ./kubectl-apply.sh from the k8s directory. You should see a bunch of created messages.

    deployment.apps "healthpoints" created
    deployment.extensions "healthpoints-elasticsearch" created
    service "healthpoints-elasticsearch" created
    deployment.extensions "healthpoints-postgresql" created
    service "healthpoints-postgresql" created
    service "healthpoints" created

    You can use kubectl get pods and kubectl logs -f {podName} to see the logs.

    $ kubectl get pods
    NAME                                          READY     STATUS    RESTARTS   AGE
    healthpoints-6b56d9d646-h9cn2                 1/1       Running   0          3m
    healthpoints-elasticsearch-84cf759984-vwhv8   1/1       Running   0          18m
    healthpoints-postgresql-56ddd4bfc9-mptch      1/1       Running   0          18m
  7. Run kubectl get svc healthpoints to get the external IP of your application on Google Cloud. Open http://<external-ip>:8080 to view your running application.

  8. Update your Okta application to have the app’s IP address as a Login redirect URI (e.g., http://<external-ip>:8080/login). Then, verify everything works.

  9. Scale your app as needed with kubectl:

    kubectl scale --replicas=3 deployment/healthpoints

    Run kubectl get pods to watch your pods startup.

    NAME                                          READY     STATUS    RESTARTS   AGE
    healthpoints-6b56d9d646-5lmjk                 0/1       Running   0          14s
    healthpoints-6b56d9d646-h9cn2                 1/1       Running   0          7m
    healthpoints-6b56d9d646-vsm4r                 0/1       Running   0          14s
    healthpoints-elasticsearch-84cf759984-vwhv8   1/1       Running   0          23m
    healthpoints-postgresql-56ddd4bfc9-mptch      1/1       Running   0          23m

The result? A Spring Boot API running in production on GKE!! Wahoo!

JHipster API on GKE

React Native + Spring Boot on GCP

To make your React Native app work with your GCP instance, you just need to modify its AppConfig.js file to point to its URI, then package and deploy.

export default {
  apiUrl: 'http://<external-ip>:8080',
  appUrlScheme: 'healthpoints'
}

Secrets: Change the API URL for Different Environments

You might think it’s a pain that you had to change the API URL for the different environments (local development vs. Cloud Foundry vs. Google Cloud). I agree!

Luckily, react-native-config is already built-in to Ignite JHipster. This project allows you to expose config variables to your JavaScript code in React Native. You can store API keys, URLs, and other sensitive information in a .env file.

API_URL=https://production-is-awesome.cfapps.io

To use react-native-config in your application, use the following steps:

  1. Copy .env.example to .env and update .gitignore to ignore it.

  2. Add your config variables.

  3. Follow instructions at luggit/react-native-config#setup.

  4. Change your AppConfig.js to the following:

import Secrets from 'react-native-config'

export default {
  apiUrl: Secrets.API_URL,
  appUrlScheme: 'healthpoints'
}

Explore React Native, Spring Boot, and JHipster

This tutorial showed you how to build a secure Spring Boot API (powered by JPA, PostgreSQL, and Elasticsearch) with just a few commands. Then you learned how to run your app with Gradle, use Docker to run external services, and verify you had high-quality code with Sonar. And that was just the beginning!

I showed you how to build a React Native app, again with just a few commands. Heck, even deploying to the cloud only took a handful of interesting CLI commands.

The source code for this tutorial is available on GitHub @oktadeveloper/okta-react-native-spring-boot-example.

Don’t you feel hip playing with all these best-of-breed technologies? I sure do! 🤓

Wanna stay hip? Check out some of our other tutorials on React Native, Spring Boot, and JHipster.

Follow us, watch our videos, and connect with us if you’d like to learn more about best-of-breed open source technologies. We’re big fans of open source. 💙