Skip to content

Resource Type Naming: singular or plural? What to choose? Why is that? ​

Every resource in WarpDrive has a type: the type string that identifies it in API responses, in its ResourceSchema, in the relationship fields that point to it and in the requests that fetch it. WarpDrive has no rule about what that string looks like. It can be singular or plural, dasherized or snake_case. It only has to be the same string everywhere.

Why older apps use singular, dasherized types ​

The legacy serializers in @warp-drive/legacy singularize and dasherize every type they receive, so apps that use them end up with types like user-setting no matter what the API sends.

Without those serializers, WarpDrive uses the type your API sends as-is, and normalizing it is up to you.

So what to choose? ​

Whatever your API already sends. You can choose singular or plural names, but it is usually best to let the backend decide and treat the type as part of your app's API contract, rather than renaming every type on the frontend. Whichever you choose, stick with it. Be Consistent!

What does consistency look like? ​

Let's say your convention is singular dasherized, e.g. user-setting ​

  • The API responds with "type": "user-setting", or a handler normalizes the type to that.

  • The ResourceSchema's type and every relationship field's type that points to it use the same string:

    schemas/user.ts
    ts
    import { withDefaults } from '@warp-drive/legacy/model/migration-support';
    
    export const UserSettingSchema = withDefaults({
      type: 'user-setting',
      fields: [{ kind: 'attribute', name: 'value' }],
    });
    
    export const UserSchema = withDefaults({
      type: 'user',
      fields: [
        {
          kind: 'hasMany',
          name: 'userSettings',
          type: 'user-setting',
          options: { async: false, inverse: null },
        },
      ],
    });
    
    // wherever you configure the store
    store.schema.registerResources([UserSchema, UserSettingSchema]);
  • Requests made with a request builder use it too:

    ts
    import { findRecord } from '@warp-drive/utilities/json-api';
    
    const { content } = await store.request(findRecord('user-setting', '1'));

But what about plural and snake case? ​

The same places use user_settings instead:

  • The API responds with "type": "user_settings".

  • The schema and the relationship field use it:

    ts
    export const UserSettingSchema = withDefaults({
      type: 'user_settings',
      fields: [{ kind: 'attribute', name: 'value' }],
    });
    
    // in UserSchema's fields
    {
      kind: 'hasMany',
      name: 'userSettings',
      type: 'user_settings',
      options: { async: false, inverse: null },
    }
  • So do requests:

    ts
    const { content } = await store.request(findRecord('user_settings', '1'));

The example schemas use LegacyMode, which despite its name is the recommended mode today. The resource type rules are the same in PolarisMode and for apps still defining Model classes.

💡 The type and the URL are separate

The findRecord builder from @warp-drive/utilities/json-api builds the URL path by pluralizing the type you pass. If your endpoint uses a different path, keep the type as it is and pass the resourcePath option to findRecord instead.

But what about the JSON:API spec? ​

The JSON:API spec is agnostic about how type is named. Here is the quote from the spec:

Note: This spec is agnostic about inflection rules, so the value of type can be either plural or singular. However, the same value should be used consistently throughout an implementation.

You can read more about it in the JSON:API spec.


Released under the MIT License.