Shivam Manswalia
Shivam Manswalia

Reputation: 1323

How to connect Vue.js as frontend and FastAPI as backend?

I'm building a project On Jobs Portal and I need Vue as frontend and FastAPI as backend for add, delete, update. I want to know if I can connect these both or not.

Upvotes: 9

Views: 20662

Answers (3)

ccsv
ccsv

Reputation: 8659

Here are two solutions for using Vue for the entire frontend you can go modify the vue.config.js file:

const { defineConfig } = require('@vue/cli-service')
const path = require('path');

module.exports = defineConfig({
  transpileDependencies: true,    //Transpile your dependencies
  publicPath: "/static",          //Path of static directory
  outputDir: path.resolve(__dirname, '../static'),   // Output path for the static files
  runtimeCompiler: true,
  devServer: {
    // Write files to disk in dev mode, so FastAPI can serve the assets
    port: 8080,
    devMiddleware: {
      writeToDisk: true,
    }
  },
})

Then in the vue-cli you can run either build or serve (this is in the task page) or in the console run

npm run build

This will transpile your staticfiles to the output folder you set on in the vue.config.js file. Next are two options with and without jinja2

Without Jinja

Next go to main.py on Fastapi and add the two lines below

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles   #<-- Add this

app = FastAPI()


#static files & load
app.mount("/", StaticFiles(directory="static", html = True), name="static") #<-- Add this

There should be a index.html page in the static folder the line with the app.mount("/", StaticFiles(directory="static", html = True), name="static") sets the directory for the path to the static file and loads the html file to "/".

You can add, delete, update, and query using a GraphQL library

With jinja2

Go to main.py on Fastapi and add the two lines below

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates 
from fastapi.staticfiles import StaticFiles   

app = FastAPI()


#static files & load
app.mount("/static", StaticFiles(directory="static", html = True), name="static") 
templates = Jinja2Templates(directory="static")

@app.get("/")
async def serve_home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Here the index.html page that is transpiled is loaded in as a jinja template. You can write your GET and POST functions on the main.js

Upvotes: 2

Khanh Luong
Khanh Luong

Reputation: 542

You can do it, in quite a clean way IMO. The best I can come up with is using Node.js/npm to bundle the Vue app to a dist/ folder (by default)

vue-cli-service build

I then use the project folder structure like this

├── dist/ <- Vue-CLI output
    └── index.html
├── src/ <- Vue source files
└── app.py

To archieve the same project setup, you can just simply init a Vue project using vue create and create your Python project in a same folder (using a IDE like Pycharm for example, make sure you don't override one with the other).

Then you can use FastAPI.staticfiles.StaticFiles to serve them

# app.py

app.mount('/', StaticFiles(directory='dist', html=True))

Remember to put the above app.mount() line after all other routes though, since it will override every route that comes after.

You can even use vue-cli-service build --watch so that every change in Vue code will be reflected in the HTML file right after, and all you need to do is to press F5 on your browser to see those changes.

You can change the dist folder output to something else too, using vue-cli-service build --dest=<folder name> and change directory parameter in app.mount() line above. (according to Vue-CLI docs)

Here is one of my projects using that setup: https://github.com/KhanhhNe/sshmanager-v2

Upvotes: 6

Yagiz Degirmenci
Yagiz Degirmenci

Reputation: 20618

I want to know if i can connect these both or not.

Quick answer: Yes, you can connect.

But there are many ways, you can render templates with something like Jinja or you can create a completely different project with something like Vue CLI and you might want to use something like Nginx or Apache etc to connect both.

So let's create an example app with Jinja

Structure

├── main.py
└── templates
    └── home.html

Backend- main.py

  • We are serving our frontend in / and rendering our home.html in that path.
  • We are using the templates folder to keep our HTML's and passing it to Jinja.
  • Also, we are going to send a request to /add from our front-end.
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel

templates = Jinja2Templates(directory="templates") 

app = FastAPI()


class TextArea(BaseModel):
    content: str


@app.post("/add")
async def post_textarea(data: TextArea):
    print(data.dict())
    return {**data.dict()}


@app.get("/")
async def serve_home(request: Request):
    return templates.TemplateResponse("home.html", {"request": request})

Frontend - home.html

  • Let us create a dummy app that has a textarea and button.
  • We are using Axios to send our requests to backend.
  • Since they are running on the same port we can pass /add to Axios directly.
<html>
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<body>
    <div id="app">
        <textarea name="" id="content" cols="30" rows="10" v-model="content"></textarea>
        <button @click="addText" id="add-textarea">click me</button>
    </div>

    <script>
        new Vue({
            el: "#app",
            data: {
                title: '',
                content: ''
            },
            methods: {
                addText() {
                    return axios.post("/add", {
                        content: this.content
                    }, {
                        headers: {
                            'Content-type': 'application/json',
                        }
                    }).then((response) => {
                        console.log("content: " + this.content);
                    });
                }
            }
        });
    </script>
</body>

</html>

In the end, you will have a terrible looking textarea and a button. But it will help you to understand things better.

 {'content': 'Hello textarea!'}
INFO:     127.0.0.1:51682 - "POST /add HTTP/1.1" 200 OK

Upvotes: 22

Related Questions