Skip to content
/ opus Public

Lightweight and convenient builder-based strictly-typed GraphQL client

Notifications You must be signed in to change notification settings

tilework/opus

Repository files navigation

Opus

Opus, the Latin for ‘work’, is the way in which the pieces of a tilework are cut and placed.

Why

Make your requests extensible

The GraphQL requests generated by this library are easily extensible by the plugin system, because they are generated procedurally, as opposed to the commonly-used generation from strings.

Stay lightweight

This library is TINY and still provides all of the basic functionality of GraphQL interactions. Additional features are planned to get implemented on-demand.

Definitely typed

Generate GraphQL requests with Builder pattern and receive properly structurally typed responses upon fetching!

Hnet com-image

What

Building fields

Almost every aspect of GraphQL functionality is supported: fields, nested fields, inline fragments, arguments. The only thing not yet supported are non-inline Fragments. Although, apart from slightly increased request size, this will not impact your development experience in any way.

import { Query, Field, InlineFragment } from '@tilework/opus';

const dragonFields = ['name', 'neck_length', 'age'] as const;

const dragonsQuery = new Query('dragons', true) // `true` means 'expect array'
    .addArgument('limit', 'Int', 5)
    .addFieldList(dragonFields)
    .addField(new Field('children', true)
        .addFieldList(dragonFields)
    )
    .addField(new InlineFragment('Fire')
        .addField('fire_temperature')
    )
    .addField(new InlineFragment('Ice')
        .addField('ice_density')
    )

Getting data type from a field

Sometimes it is necessary to explicitly reference the type, which the fetched data will have upon retrieval. A utility type is provided in order to make this possible!

import { Query } from '@tilework/opus';
import type { DataType } from '@tilework/opus';

const query = new Query('person', true)
    .addFieldList(['name', 'surname']);

let result: DataType<typeof query>;

result = await client.post(query);

Calculated fields

An opportunity to derive additional data from the fetched information is provided with calculated fields. A calculated field can be added on any instance of Field, Query or Mutation.

Such fields get calculated once, when the request is post-processed upon fetching. They are calculated starting with the deepest child and going up to the root node (post-visit).

import { Query, Field } from '@tilework/opus';

const query = new Query('dragons', true)
    .addField('active')
    .addField(new Field('launch_payload_mass')
        .addField('kg')
        .addCalculatedField('lb', (result) => result.kg * ONE_KG_IN_LBS)
    )
    .addField(new Field('return_payload_mass')
        .addField('kg')
    )
    .addCalculatedField('payload_delta_kg', (result) => {
        return result.launch_payload_mass.kg - result.return_payload_mass.kg;
    });

Client-side transformations

Note: Attempts to add fields to the result via this API will throw due to the processable object being sealed at the time of processing. Use calculated fields to add additional fields.

Sometimes it's necessary to modify the data you have received, e.g. in order to reduce nesting of some fields. In order to achieve that, this functionality should be used!

In the example below, the property launch_payload_mass becomes launch_payload_mass.kg, just to make everything a bit more convenient.

import { Query, Field } from '@tilework/opus';

const query = new Query('dragons', true)
    .addField('active')
    .addField(new Field('launch_payload_mass')
        .addField('kg')
        .addTransformation((launchPayload) => launchPayload.kg)
    )
    .addField(new Field('return_payload_mass')
        .addField('kg')
        .addTransformation((returnPayload) => returnPayload.kg)
    );

...

typeof result.dragons[0].launch_payload_mass; // number

Fetching some requests!

The client provides an opportunity to fetch queries and mutations, as well as fetch combined queries and combined mutations.

import { client, CombinedField } from '@tilework/opus';

// Single requests
const queryResult = await client.post(someQuery);
const mutationResult = await client.post(someMutation);

// Combined queries and mutations work the same
const combinedQueryResult = await client.post(new CombinedField
    .add(firstQuery)
    .add(secondQuery)
);

Configuring the client

It is necessary to set up the client before fetching any data. See the required configuration steps below.

  • Endpoint
    • client.setEndpoint(endpoint: string) allows to configure this in runtime
    • Setting GRAPHQL_ENDPOINT in process.env will also set the endpoint
    • It defaults to /graphql
  • Headers
    • client.setHeaders(headers: any) will set the headers to use for fetching requests

About

Lightweight and convenient builder-based strictly-typed GraphQL client

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •