Shopify / shopify-app-js

MIT License
297 stars 117 forks source link

Admin API GraphQL Codegen does not generate types for scalars #1154

Open sleepdotexe opened 4 months ago

sleepdotexe commented 4 months ago

Issue summary

Before opening this issue, I have:

I am using the GraphQL Codegen to automatically generate TypeScript types for my Shopify queries. I am running into issues using properties that have Scalar types, as these are shown as type any. For example, Product['created_at'] has type any, when it really should be string since it has the scalar type of DateTime which is an extension of a string.

The helper functions from @shopify/api-codegen-preset don't seem to provide scalar types by default, nor any way to manually provide these types. Since these types are managed by Shopify and relate specifically to the API, it would make the most sense to me for these to be automatically provided by the helper setup functions.

According to https://the-guild.dev/graphql/codegen/plugins/typescript/typescript#scalars, it should theoretically be possible to provide scalars to the codegen.

Here is an example .graphqlrc.ts:

import { ApiType, shopifyApiProject } from '@shopify/api-codegen-preset';

import CONSTANTS from './utils/constants';

const docs = [
    './utils/shopify/queries/**/*.{js,ts,jsx,tsx}',
    './utils/shopify/mutations/**/*.{js,ts,jsx,tsx}',
    './utils/shopify/fragments/**/*.{js,ts,jsx,tsx}',
];

const { apiVersion } = CONSTANTS.shopify; // ApiVersion.January24

export default {
    schema: `./types/shopify/admin-${apiVersion}.schema.json`,
    documents: docs,
    projects: {
        default: shopifyApiProject({
            apiType: ApiType.Admin,
            apiVersion: apiVersion,
            documents: docs,
            outputDir: './types/shopify',
        }),
    },
};

Expected behavior

Using the .graphqlrc.ts above, the inbuilt Shopify Scalars should be automatically generated in admin.types.d.ts like so:

/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: { input: string; output: string; }
  String: { input: string; output: string; }
  Boolean: { input: boolean; output: boolean; }
  Int: { input: number; output: number; }
  Float: { input: number; output: number; }
  ARN: { input: any; output: any; }
  Date: { input: string; output: string; }
  DateTime: { input: string; output: string; }
  Decimal: { input: string; output: string; }
  FormattedString: { input: string; output: string; }
  HTML: { input: string; output: string; }
  JSON: { input: any; output: any; }
  Money: { input: string; output: string; }
  StorefrontID: { input: string; output: string; }
  URL: { input: string; output: string; }
  UnsignedInt64: { input: string; output: string; }
  UtcOffset: { input: string; output: string; }
};

...Which allows the properties to have correct types in files.

products[0].createdAt
            ^----- `createdAt: string | undefined`

Actual behavior

The Shopify scalars have types of any.

/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: { input: string; output: string; }
  String: { input: string; output: string; }
  Boolean: { input: boolean; output: boolean; }
  Int: { input: number; output: number; }
  Float: { input: number; output: number; }
  ARN: { input: any; output: any; }
  Date: { input: any; output: any; }
  DateTime: { input: any; output: any; }
  Decimal: { input: any; output: any; }
  FormattedString: { input: any; output: any; }
  HTML: { input: any; output: any; }
  JSON: { input: any; output: any; }
  Money: { input: any; output: any; }
  StorefrontID: { input: any; output: any; }
  URL: { input: any; output: any; }
  UnsignedInt64: { input: any; output: any; }
  UtcOffset: { input: any; output: any; }
};

...Which means properties are not typed correctly.

products[0].createdAt
            ^----- `createdAt: any`

Steps to reproduce the problem

  1. Set up a typescript project using the Admin GraphQL API and codegen as per these instructions.
  2. Use the .graphqlrc.ts file as outlined above in this issue.
  3. Attempt to use a scalar property (eg. Product['createdAt']).

Debug logs

There is a temporary solution I've found – after using the shopifyApiProject() helper function, I managed to manually force the config to include scalars like so:

const outputDir = './types/shopify';
const { apiVersion } = CONSTANTS.shopify;

const setup: ReturnType<typeof shopifyApiProject> & {
    extensions: {
        codegen: { generates: { [key: string]: { config?: { scalars?: any } } } };
    };
} = shopifyApiProject({
    apiType: ApiType.Admin,
    apiVersion: apiVersion,
    documents: docs,
    outputDir,
});

setup.extensions.codegen.generates[`${outputDir}/admin.types.d.ts`].config = {
    minify: true,
    scalars: {
        Color: 'string',
        Date: 'string',
        DateTime: 'string',
        Decimal: 'string',
        FormattedString: 'string',
        HTML: 'string',
        Money: 'string',
        StorefrontID: 'string',
        URL: 'string',
        UnsignedInt64: 'string',
        UtcOffset: 'string',
    },
};

export default {
    schema: `${outputDir}/admin-${apiVersion}.schema.json`,
    documents: docs,
    projects: {
        default: setup,
    },
};

This seems to work, as the admin.types.d.ts file now includes the expected types for scalars. However it's not my favourite solution since it relies on hardcoding the scalar types, and also updating them if they are change (or new scalars are added) in future. Additionally, I have to mutate the setup object and provide a hack-y union type to keep type safety.

Could we have these scalars included by default? And if not, could we at least have a documented option added to the shopifyApiProject helper function that allows us to manually provide these without the workaround?

matteodepalo commented 4 months ago

Hi @sleepdotexe this is a great issue description and a good suggestion. We'll take a look at what we can add to shopifyApiProject, but in the meanwhile I'm glad that your workaround is working for you.

github-actions[bot] commented 2 months ago

We're labeling this issue as stale because there hasn't been any activity on it for 60 days. While the issue will stay open and we hope to resolve it, this helps us prioritize community requests.

You can add a comment to remove the label if it's still relevant, and we can re-evaluate it.

StepanMynarik commented 2 months ago

+1

github-actions[bot] commented 3 weeks ago

We're labeling this issue as stale because there hasn't been any activity on it for 60 days. While the issue will stay open and we hope to resolve it, this helps us prioritize community requests.

You can add a comment to remove the label if it's still relevant, and we can re-evaluate it.

StepanMynarik commented 3 weeks ago

Still an issue I would love to see resolved, not stale.