Integrating Vapor and React (Vapor 2 and Xcode 9.0)

1137 Views

This tutorial will detail how to set up Vapor using react front end, as 2 separate projects. This tutorial assumes you have npm and react already installed.

Creating the React Project

Let's begin on the react side. First, create a react app:

    $ create-react-app vaporreact

This may take some time. After, cd into the project:

    $ cd vaporreact

and run npm start, and you'll see the Welcome to React default page.

So now we have a basic react app, and it is working. We will need a way for our react app to call our vapor backend, and we shouldn't use traditional ajax requests in React. So to do so, we will use the library, whatwg-fetch, found here: https://www.npmjs.com/package/whatwg-fetch. This library is included in most modern browsers.

You can check if the fetch library is included in your browser, here

  • Tip: If the fetch library is not included in your browser, you can install it with the following command: $ npm install --save whatwg-fetch

Creating the Vapor Project

Now, let's switch to vapor. Create a basic vapor app by running:

    $ vapor new vaporreactbackend --template=api

then, $ cd vaporreact , and then $ vapor xcode to create the Xcode project.

Then on the top left of Xcode, switch the target from vaporreact-Package to Run, and then run your project. Navigate to http://localhost:8080/hello and you should see:

    {"hello":"world"}

Good! this means everything is set up correctly on both ends so far. Now we just need a way to query our api from vapor, and display the data in our react app.

Combining Vapor and React

In your react project, create a file called ApiHelper.js. Add the following code to the empty file:

    import 'whatwg-fetch'
    class ApiHelper {
      static getHelloWorld() {
       const url = 'http://localhost:8080/hello';
        return fetch(url, { method: 'GET' })
       .then(function(response) {
          return response.json();
        })
      }
    } 

    export default ApiHelper

The, in App.js, replace the entire contents of the file with the following:

    import React, { Component } from 'react';
    import './App.css';
    import ApiHelper from './ApiHelper.js' // (1)

    class App extends Component {

     // (2)
      constructor(props) {
        super(props);
        this.state = {helloWorldText: 'not loaded yet'}
      }

      componentWillMount() {
        this.getHelloWorld(); // (3)
      }

      // (4)
      getHelloWorld() {
        ApiHelper.getHelloWorld().then((result) => {
          this.setState({helloWorldText: result.hello})
        }).catch((error) => {
            console.log("error is " + error);
        });
      }


    // (5)
      render() {
        return (
          <div>
           Hello  {this.state.helloWorldText}
          </div>
        );
     }
    }

    export default App;

That's quite a bit of code! let's go through it, step by step:

At 1, we import our ApiHelper class, so we can use the function we previously wrote in this file.

At 2 We set our initial state. Our text to display at first will be "not loaded yet", to indicate that if you see this text, then our react project has not yet loaded the data from the vapor backend.

At 3 Before the component mounts, we call our function to retrieve the data.

At 4 We write a function to use call our ApiHelper class. If successful, we return the data and set the state with it, otherwise console.log the error. The value of "result" when properly fetched will be {hello: world} in our case. So by calling result.hello, we will extract the word 'world'.

And at 5, we simply print "Hello", plus whatever the current value of this.State.helloWorldText is.

Run npm start and load the app now. However, there is an error! Have a look at the console:

Failed to load http://localhost:8080/hello: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

This is a CORS error. CORS stands for Cross Origin Resource Sharing, and CORS rules essentially say that in order to interact with a given domain (like localhost:xyz), then the origin must also be on the same domain. This is for security purposes. However, we are attempting to access localhost:8080 from localhost:3000, which is against the rules.

However, there is a simple workaround. In your vapor project, add a new file: cors.json. Add the following to the empty file:

    {
     "allowedOrigin": "*",
     "allowedMethods": ["GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"],
     "allowedHeaders": ["Accept", "Authorization",
     "Content-Type","Origin", "X-Requested-With"]
    }

Note, that you can adjust the settings based on your security needs--you can restrict this to only GET methods, for example, or add/remove which headers are allowed. The * for allowedOrigin means that we will accept requests from any origin. For this tutorial, for example, we could restrict it to only allow accesss from localhost:3000, like so: "allowedOrigin": "http://localhost:3000", and it would work just the same.

Next, go to droplet.json, and change the middleware to include the cors middleware, like so:

    "middleware": [
        "cors",
        "error",
        "date",
        "file"
    ],

Now, restart your server, and navigate back to localhost:3000/, and you should see "Hello World" instead of the "Hello not loaded yet" text.

And this is it! that's all you need to get started using vapor as a backend with React as a front end. There are plenty of other pre-set routes for you in Routes.swift if you would like to play around with them and load different data for testing purposes. Setting up a separate front end and backend is fairly simple and straightforward, just the cors error is the main thing to look out for.

Thanks for reading! Questions/comments welcome below :)



Enjoy this article? Consider supporting VaporForums by following us on Twitter! Get the latest Vapor articles, tutorials, and news.