DEV Community

tnodell
tnodell

Posted on

Creating A New Data Provider In Graphweaver

Recently I was working on creating a new data provider for Graphweaver, our open source tool for connecting multiple data sources. Since there may be others in the future creating a data source from scratch I thought it would be valuable to document the process here.

Step 1: Create a Data Provider

Creating a new data provider requires extending the base graphweaver provider. This defines how data providers interact with Graphweaver. You will need to extend this class to implement your new provider. All of the base data provider methods can be found in these docs.

This example only implements the find and findOne functions, which gets entities and is used on the adminUI on the dashboard and details panel, respectively.

src/packages/core/src/base-provider.ts

export class Provider<D, G> implements BackendProvider<D, G> {
    constructor(readonly backendId: string) {
        if (!backendId) throw new Error('BackendId must be defined');
    }

    // READ METHODS
    public async find(
        filter: Filter<G>,
        pagination?: PaginationOptions,
        additionalOptionsForBackend?: any
    ): Promise<D[]> {
        throw new Error('Find not implemented.');
    }
Enter fullscreen mode Exit fullscreen mode

The new provider accepts a generic DataEntity and GraphQLEntity (from graphweaver/core). It will include a private property of your base entity for the service you’re integrating with (more on the base entity and service in a bit). A constructor method with the backendId calls the provider class with the entity that is passed in. You can implement the remaining CRUD methods from the Graphweaver Base Provider. Those methods will call the service that interfaces with the new data source you’re integrating with.

export class YourBackendProvider<
  D extends BaseDataEntity,
  G extends GraphQLEntity<D>
> extends Provider<D, G> {
  private entity: YourEntity<D>;

  constructor(backendId: string, entity: YourEntity<D>) {
    super(backendId);
    this.entity = entity;
  }

  public async findOne(filter: Filter<G>): Promise<D | null> {
    const yourService = new YourService(this.entity);
    return yourService.getEntity(String(filter.id));
  }

public async find(filter: Filter<G>): Promise<D[]> {
    const salesforce = new Salesforce(this.entity);
    return salesforce.getEntities(filter);
  }
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Base Data Entity

Now we’ll need to to define the base data entity representing the new data source. This will implement the BaseDataEntity type in Graphweaver. The BaseDataEntity includes methods to determine if the entity is a reference or a collection.

Your entity will have three properties:

  • new (data: any): D; Any class using this interface must have a constructor that takes some data and returns an instance of your data entity.
  • entityName: string; You must provide a name for the entity
  • getProperties: () => string; You must provide a method that gets the properties of the entities and returns them. In this case we return a string that goes into constructing a GraphQL query to our new data source.

Your base entity class is an abstract class that implements the above type.

export interface YourEntity<D extends YourBase> {
  new (data: any): D;
  entityName: string;
  getProperties: () => string;
}

export abstract class YourBase implements BaseDataEntity {
  static entityName: string;

  static getProperties() {
    // return what you need to get back data from your new source
   return `
        Id
        Name { value  }
    `;
  }

  public isCollection(fieldName: string, dataField: any) {
    return false;
  }

  public isReference(fieldName: string, dataField: any) {
    return false;
  }
}
Enter fullscreen mode Exit fullscreen mode

Next we’ll create an example entity from this base. This entity will be mapped when we return data from our service.

import { Base } from "./base";
interface ExampleEntityData {
    id: string;
    name: string;
}

export class ExampleEntity extends Base {
    static entityName = "Example"; //This goes into the GQL request
    id!: string;
    name!: string;

    constructor(data: ExampleEntityData) {
        super();
        this.id = data.id;
        this.name = data.name;
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Service

Now we can implement our connection to our new data source. This service will reference your base entity. It’s up to you to implement the functions that will request data from your data source. In this example let’s make a query to some GraphQL endpoint. When the data returns, we’ll map it to the entity we defined earlier.

export class YourService<D extends Base> {
  private entity: YourEntity<D>;

  constructor(entity: YourEntity<D>) {
    this.entity = entity;
  }

  public async getEntities(filter: YourFilter<D>) {
    // Get the entities
    const query = `query {
                    result: ${this.entity.entityName}${filter ? constructFilter(filter) : ""} {
                        edges {
                            node {
                                ${this.entity.getProperties()}
                            }
                        }
                    }
        }`;

    const response = await fetch("your-endpoint.com", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
        Authorization: `Bearer ${process.env.TOKEN}`,
      },
      body: JSON.stringify({
        query,
      }),
    });

    const data = await response.json();

    // Map new Entities from the data
    return data.map((data) => {
      return new this.entity({ id: data.id, name: data.name });
    });
  }
Enter fullscreen mode Exit fullscreen mode

Conclusion

Now you have created a new data integration with Graphweaver. By following these three steps – creating a data provider, defining a base data entity, and implementing the connection to the service – developers can extend Graphweaver's capabilities to integrate with any data source.

Top comments (0)