Lighthouse GraphQL casts ID to string instead of Integer?

1.4k Views Asked by At

I have an app that uses Laravel and Lighthouse on the backend, and Nuxt and Vuex-ORM/graphql-plugin on the frontend. I'm having a difficult time with comparisons because for whatever reason, sometimes data is queried or mutated and IDs seem to be a mixture of either integer or string.

I'm in the midst of an effort to make sure that every single ID is in fact an integer. Whenever I console.log a Trip on the frontend, the ID of that Trip is still a string. When I run the query in graphql-playground, it returns as a string however I think that's more because it's an "ID" scalar type which can be either an int or string. Or something. I don't know, I'm still pretty green with this.

Trip.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Trip extends Model
{
    protected $fillable = [
        'name', 'description', 'user_id'
    ];

    // automatically eager load todos and shopping_list_items
    protected $with = [
        'items', 'users'
    ];

    // define relationships
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class);
    }
}

trip.graphql:

extend type Query @guard(with: ["api"]) {
    trip(id: ID! @eq): Trip! @find
    trips: [Trip]! @field(resolver: "TripsQuery@find_by_user")
}

extend type Mutation @guard(with: ["api"]) {
    updateTrip(id: ID! trip: TripInput! @spread): Trip @update
}

type Trip {
    id: ID!
    name: String
    description: String
    created_at: DateTime!
    updated_at: DateTime!
    user_id: ID

    items: [Item]! @hasMany
    users: [User]! @hasMany
}

input TripInput {
    name: String
    description: String
    user_id: ID
}

trip.js:

import { Model } from '@vuex-orm/core';
import Item from './item';
import TripUser from './tripUser';
import User from './user';

export default class Trip extends Model {
  static entity = 'trips';
  static eagerLoad = ['items', 'users'];

  static fields () {
    return {
      id: this.number(0),
      name: this.string(''),
      description: this.string(''),
      user_id: this.number(0),

      // relationships
      items: this.hasMany(Item, 'trip_id'),
      users: this.belongsToMany(User, TripUser, 'trip_id', 'user_id')
    };
  }
};

planning.vue:

<script>
  import currentUser from '~/mixins/currentUser';
  import Trip from '~/data/models/trip';
  import tripsQuery from '~/apollo/queries/content/trips.gql';

  export default {
    name: 'Planning',

    middleware: 'authenticated',

    mixins: [currentUser],

    data: () => ({
      loading: 1,
    }),

    computed: {
      trips () {
        return Trip.query().where('owner_id', this.currentUser.id).all();
      }
    },

    async mounted () {
      const { trips } = await this.$store.dispatch('entities/simpleQuery', {
        query: tripsQuery,
        variables: {},
        bypassCache: false
      });
      console.log(typeof trips[0].id); // <--- returns 'string';
      Trip.insert({ data: [...trips] });
      this.selectedTrip = trips.length ? trips[0] : null;
      this.loading = 0;
    },

    head () {
      return {
        title: 'Planning'
      };
    }
  };
</script>

You can see in the console.log of the mounted function in the Vue file, that the id is still returning as a string instead of an integer. How do I make sure it is always an integer instead?

UPDATE: I think I've narrowed it down to a larger issue as a whole with the @vuex-orm/plugin-graphql library I'm using. I was attempting to revert a bit so that everything used string id's instead, however I discovered that when I dispatch a mutation, for some reason there is code that transforms ids into integers. I'm going to file a bug report with that library, and close this question.

1

There are 1 best solutions below

2
On

I wanted to chime in here and point out that the ID is being cast as a string by design, not as a bug. You seen read more about it in this SO answer: https://stackoverflow.com/a/47888604/11678503

In short, the ID is a scalar type defined within the GraphQL specification (June 2018 working spec):

The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, it is not intended to be human‐readable. While it is often numeric, it should always serialize as a String.

While ID's in a database are commonly an auto-incrementing numeric key, this is by no means a standard. Many API's use hashes and UUID's instead, as it depends on the use-case. As such, an ID can always be a string, but it cannot always be a number — hence why casting as a string is a safer bet.

So I hope that clears up why Lighthouse and, by extension, the graphql-php library cast IDs as strings. They're keeping with the spec.

Hope that helps clear things up!