Search code examples
djangovue.jsgraphqlvue-routervue-apollo

URL management with Django, GraphQL, Apollo and VueJS


As said in the title, I'm using Django, GraphQL, Apollo and VueJS in my project. I'm developping it as a SPA (Single Page Application).

Everything works fine, until I hit the F5 button and refresh the page. Indeed, it shows an unknown page. The thing is it is VueRouter that is managing the SPA and it works fine. But when I press F5, that is Django that tries to serve a page for the current URL and since it doesn't know it, it can't serve the appropriate page.

I know I can set the VueRouter 'history' mode, which I did, and add a URL to Django that serves index.html whatever the URL is.

My problem is the following : When I'm on a particular form view (i.e : a User form view) my URL is the following :

http://localhost:8000/user

Since I'm using GraphQL for my API, the retrieved data is not based on the URL. In fact, that is my VueJS component that says : Hey Apollo, run that GraphQL to retrieve the User I want.

So when I refresh, yes it serves the User form view..but empty.

The question is : How could I solve this ?

For clarification purposes, here are some code samples :

My Django URLs :

# (... other imports here ...)
from .schema import schema

urlpatterns = [
    path('admin/', admin.site.urls),
    path('graphql', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),  # 'schema' is the main GraphQL schema
    path('', TemplateView.as_view(template_name='index.html')),
    re_path(r'^.*$', TemplateView.as_view(template_name='index.html'))  # I saw that many times to serve the page whatever the URL is when refreshing the page
]

My Vue Router :

export default new Router({
  mode: 'history',
  routes: [
    { path: '/', name: 'MainApp' },
    // ...
    { path: '/users', name: 'UserList', component: UserList },
    { path: '/user/create', name: 'UserFormCreate', component: UserForm, props: true },
    { path: '/user', name: 'UserFormView', component: UserForm, props: true },
    { path: '/user/edit', name: 'UserFormEdit', component: UserForm, props: true },
    // Same pattern for other models like 'Group' ...
  ]

My Example VueJS Component :

<script>
import {
  // ...
  USER_QUERY,
  // ...
} from '../../graphql/base/user.js'

export default {
  name: 'UserForm',
  props: {
    userId: Number,
    editing: {
      type: Boolean,
      default: false
    }
  },
  apollo: {
    user: {
      query: USER_QUERY,
      variables () { return { id: this.userId } },
      skip () { return this.userId === undefined },
      result ({ data }) {
        this.form.username = data.user.username
        this.form.firstName = data.user.firstName
        this.form.lastName = data.user.lastName
      }
    }
  },
  data () {
    return {
      form: {
        username: '',
        password: '',
        firstName: '',
        lastName: ''
      },
      // ...
    }
  },
  methods: {
    // ...
  }

I have to mention that I've seen more or less related topics but that doesn't solve my problem.

Thanks in advance for your help !


Solution

  • Edit your route paths to use params. For example:

    { path: '/user/:userId', name: 'UserFormView', component: UserForm, props: true }
    

    Now, the app will interpret any number following the user/ path as a prop called userId. (props: true is important here for using the params as props.)

    The only other change you need to make is adjusting your router-links to include the id as well (Ex.: http://localhost:8000/user/1) so that when the page is refreshed, there will be a param to read.