DEV Community

Cover image for Setting up a full-stack application on Replit
JennaSys
JennaSys

Posted on • Updated on

Setting up a full-stack application on Replit

UPDATE: The two example repls linked below that I created are currently broken thanks to the forced automated update to Nix and not worth trying to fix at this point (I have nothing against Nix though). Quite frankly, Replit has become too slow, unstable, and poorly documented for me to justify using it anymore.

Thanks to a few programming tutorials I went through a while back, I was introduced to the Replit online development platform. More recently, I thought it would be an interesting exercise to see if I could use the Replit platform to host a development environment that I had been using for the Python-based approach to React development that I’ve adopted. I had outlined this approach in the React to Python book and picked a few examples from the book to use as my test cases.

While the Replit platform is pretty impressive, I went through quite a long learning curve in trying to figure out how to get all of the pieces I needed to use installed, configured, and running. In the end, I set up two separate repls using examples pulled right out of the book.

The first was for a front-end-only application and was pretty straightforward to get set up. But the second application was a full-stack example and proved to be a bit more challenging to get configured. Since it wasn't immediately obvious how to run multiple servers in a single Replit repl, I thought I'd document my solution here. While my development stack may be a bit different than most, the approach here would still apply to many situations.

Replit basics

Replit is kind of unique in the online development tool space since it provides you with many more options and more control over how you use it compared to most others I've seen. You can get it working with just about every programming language out there, and for the more common ones, they have templates to get you started quickly.

To create a new Replit project (or repl), you select an appropriate template like Python, TypeScript, Rust, etc., and then start adding your source files. If none of the available Replit templates are to your liking, you can also start with a Nix template that is essentially a blank slate to start building from.

One of the main features of Replit is that repls created on that platform are easily forked. This feature works much the same way as a public repository on GitHub might be forked. Except that instead of just getting a copy of the source code, you are also getting a copy of the entire development environment that was created for it. If your repl is public, even though your code is read-only to others, anyone can view it, fork it, and run it.

One last way Replit gives you to create a repl, is to pull the project source code and Replit configuration directly from a GitHub repository.

Within the Replit workspace, you have access to a file manager, code editor, Linux shell, Markdown viewer, and web browser window.

Replit Workspace

The basic setup

A typical full-stack web application will have a public-facing front-end web server and a back-end application server that is used by and/or proxied by the front-end server. In my case, I was using Flask as a back-end server that had a very simple REST API, and then I had the Parcel bundler development proxy-server for the front-end.

In addition to the servers, I also had a requirement to install and run a Python-to-JavaScript transpiler that is used in the build process. This transpiler worked in conjunction with the Parcel bundler I was using.

To automate setting up the development environment, I created a shell script that did the following:

  • Installed the specific version of Python that I needed to use
  • Installed Transcrypt (the Python-to-JavaScript transpiler)
  • Installed the JavaScript development and application dependencies for the project

I also created a shell script for starting the build process that will first check to make sure the required dependencies have been installed. If the dependencies are not installed, the build script will automatically run the setup script before running the npm start script that starts the development server.

Running two servers

If you know what you are looking for and where to look, the key to getting multiple servers working at the same time is in the Replit documentation. But even then, what is provided there isn't completely clear and kind of sidesteps this particular issue a bit.

An important key to getting the multi-server repl to work is understanding that the server started using a listening address of 0.0.0.0 becomes the public-facing web server, and starting another server listening on 127.0.0.1 keeps that server private.

When 0.0.0.0 is used, Replit accepts public requests to that server via one of its own publicly accessible IP addresses. In addition to that, it also automatically enables SSL. Regardless of what port your server is listening on, the public-facing server will be accessed via the standard SSL port (443). What this means is that, while you can use multiple ports for your internal servers, the public-facing server only accepts requests via the standard HTTPS port.

Now that we have the back-end/front-end ports figured out, the next issue is how do you run both servers simultaneously? Since both servers are blocking, you can't just chain start up commands, as the second server start command wouldn't execute until the first one ended.

One option is to fork the first command and run it in the background, allowing the second one to execute. While this works, you then don't have a convenient way of stopping the first command once you don't need it anymore.

To solve this issue, I used the npm-run-all package. This package allows you to run multiple npm scripts in parallel, and will exit out of all of them at the same time as well.

My npm scripts in the package.json file end up looking like this:

"scripts": {
  "start": "npm-run-all -p -r flask-dev parcel-dev",
  "flask-dev": "python main.py dev",
  "parcel-dev": "NODE_ENV=development parcel --log-level info src/index.html --dist-dir dist/dev --port 8080",
  "build": "NODE_ENV=production parcel build --log-level info src/index.html --no-source-maps --dist-dir dist/prod --no-cache"
},
Enter fullscreen mode Exit fullscreen mode

Using npm-run-all, the npm start script then just calls two other scripts in parallel. By using the -r or --race option, the process started by each of those scripts will be shut down when one of the other processes exits.

To build or not to build

To run your application on Replit, there is a big shiny green Run button at the top of the Replit workspace. What this button actually does is defined by the Replit template used to create the repl, but it can also be customized in the .replit file.

While it could make sense to have this button start up your development server, it may not be optimal. Because of the way Replit works when someone else wants to view your application, they essentially click that same button. If your build process takes a while, they may give up on your application starting up before the build process finishes.

So to facilitate faster startups in this situation, I prefer to just serve up pre-built application files directly with Flask when the Run button is clicked, and perform the build process and running of the dev server from the command line instead.

However, that means that the Flask server must be started up listening on different ports depending on if it is acting as the full-stack server, or if it's only acting as the back-end server. To achieve this, I added a conditional startup for Flask that is dependent on the existence of a command-line argument dev when it is started.

main.py

...

if __name__ == "__main__":
    # Start up flask in proxied mode if using parcel dev server
    if len(sys.argv) > 1 and sys.argv[1] == 'dev':
        app.run(debug=True, host="127.0.0.1", port=8000)
    else: # Run public server
        app.run(debug=True, host="0.0.0.0", port=8000)
Enter fullscreen mode Exit fullscreen mode

When Flask is started from the npm script as above:

"flask-dev": "python main.py dev"
Enter fullscreen mode Exit fullscreen mode

it will use the 127.0.0.1 address. But when started using the Replit Run button, it is configured in the .replit file to call it without the additional argument:

run = "python main.py"
Enter fullscreen mode Exit fullscreen mode

in which case it is started with the 0.0.0.0 address.

Conclusion

One general tip I can give for using Replit is to not fight their framework. Replit is opinionated in some of the tools they have pre-installed, and you are better off using what they have rather than ignoring them and just using what you may already be used to.

For example, in my case, I would typically set up a Python virtual environment using venv, and then pip install any Python dependencies I needed. When I first tried that on Replit, the results were inconsistent at best. When I went ahead and used the pre-installed Poetry package manager instead, everything went a lot smoother.

While setting up a full-stack application on Replit isn't too difficult, figuring out exactly what needs to be done may not be immediately apparent. Hopefully, my example here will give you some hints on how you might approach it.

Resources

Top comments (0)