feathersjs / docs

[MOVED] Legacy Feathers documentation
https://crow.docs.feathersjs.com/
MIT License
242 stars 532 forks source link

Typescript: How to export data model typing to the Feathers client #1514

Closed soullivaneuh closed 1 year ago

soullivaneuh commented 3 years ago

Comment/Problem

Thanks to Feathers with Typescript, I can describe the data related to my service with ease:

// src/services/billing-departments/billing-departments.class.ts
import { Service, NedbServiceOptions } from 'feathers-nedb';
import { Application } from '../../declarations';

export interface BillingDepartmentsData {
  code: string;
  saleCommission: number;
  basePrice: number;
}

export class BillingDepartments extends Service<BillingDepartmentsData> {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  constructor(options: Partial<NedbServiceOptions>, app: Application) {
    super(options);
  }
}

export default null;

And I have the type checking enabled on my service calls:

app.service('billing-department').create({
  // Got autocomplete en typecheck, yay!
});

However, this is not the case on my React Native typescript app with the feathers client:

import feathers from '@feathersjs/feathers';
import socketio from '@feathersjs/socketio-client';
import auth from '@feathersjs/authentication-client';
import { FeathersError } from '@feathersjs/errors';

const socket = io(Constants.manifest.extra.apiEndpoint, {
  transports: ['websocket'],
});
const client = feathers();
client.configure(socketio(socket));
client.configure(auth({
  storage: AsyncStorage,
}));

app.service('billing-department').create({
  // Nothing is checked here.
});

And this is normal because the typing are... on the API project! :upside_down_face:

I found that I can specify a typing on the application: https://github.com/feathersjs/feathers/blob/cf2576b0ecd9a7195cfc4936420cddaeb413cced/packages/feathers/src/declarations.ts#L64

So I assume something like this:

import { MyServiceTyping } from '@my/api';

const client = feathers<MyServicesTyping>()

However, I don't really know what I have to export exactly from the API and how to do so.

A guide about that would be very welcomed. Having type checking on my front integrations would be a real plus for code reliability. :+1:

soullivaneuh commented 3 years ago

I found this generated declarations.d.ts file:

// src/declarations.d.ts
import { Application as ExpressFeathers } from '@feathersjs/express';
import '@feathersjs/transport-commons';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ServiceTypes {}
export type Application = ExpressFeathers<ServiceTypes>;

I assume I have to play with ServiceTypes. However, each generated service overrides this declaration file like this:

declare module '../../declarations' {
  interface ServiceTypes {
    'billing-departments': BillingDepartments & ServiceAddons<any>;
  }
}

So I don't really know what to build and ship to my npm registry.

soullivaneuh commented 3 years ago

I did some test on the client side code and ended up with this:

// client.ts
import feathers, { Params, ServiceAddons } from '@feathersjs/feathers';
import { AdapterService } from '@feathersjs/adapter-commons';

export interface Service1Data {
  // Some fields
};
export interface Service2Data {
  // Some fields
};

type Service<T> = AdapterService<T> & ServiceAddons<T>;

interface ServiceTypes {
  'service-1': Service<Service1Data>;
  'service-2': Service<Service2Data>;
};

const client = feathers<ServiceTypes>();

client.service('service-1').create({
  // Got autocompletion and type check.
});

This is working so know I now I have to export the ServicesTypes.

However, the server BillingDepartments class of my previous example is a feathers-nedb service:

// src/services/billing-departments/billing-departments.class.ts
import { Service, NedbServiceOptions } from 'feathers-nedb';
import { Application } from '../../declarations';

export interface BillingDepartmentsData {
  // fields
}

export class BillingDepartments extends Service<BillingDepartmentsData> {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  constructor(options: Partial<NedbServiceOptions>, app: Application) {
    super(options);
  }
}

export default null;

As it is useless to add feathers-nedb to my front dependency (the client is just a REST client as then end), how can I export a common ServicesTypes without duplicate my code with the current api declarations (nedb-service)?

daffl commented 3 years ago

If you want to share it you can remove the individual module overrides and put them all into a centra declarationl file. The reason why it doesn't do that is because it's extremely difficult to write a generator Codemod that can do this reliably.

hanzlamateen commented 2 years ago

@daffl Can you share a sample of it?

daffl commented 1 year ago

The v5 CLI now automatically generates a typed client from the data model that will be always up to date. You can try it out here.