npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

lib-restql

v1.0.15

Published

A flexible and efficient REST API query language for frontend and backend

Downloads

81

Readme

RestQL

RestQL is a powerful and flexible library that allows you to query REST APIs using a GraphQL-like syntax. It provides a seamless way to interact with REST endpoints, offering features like batched queries, caching, and data transformation.

Features

  • Intuitive Syntax: RestQL offers a clear and precise syntax, enabling developers to articulate their data requirements succinctly.
  • Built-in Types for Data Structures: RestQL includes robust type definitions that operate at runtime, ensuring accurate data transformations and minimizing the risk of type-related errors
  • Structured API Definition: With support for a well-defined API schema, RestQL facilitates a more organized and maintainable codebase.
  • Batch Processing: By allowing the execution of multiple requests in a single operation, RestQL significantly reduces network latency and server load.
  • Advanced Caching Mechanisms: The library incorporates efficient caching strategies, optimizing both time and resource utilization.
  • Data Transformation: Custom transformations can be applied to tailor the retrieved data to specific application needs.
  • Type Safety: With TypeScript support, RestQL ensures type safety, reducing runtime errors and enhancing code reliability

Installation

npm install lib-restql

or

yarn add lib-restql

Schema Definition Language (SDL)

RestQL uses an SDL to define the structure of your API.

Type Definitions

  type ResourceName {
    field: FieldType @directive
    ...
  }
  • ResourceName: Corresponds to a REST resource (e.g., User, Post)
  • field: Represents a property of the resource
  • FieldType: Can be scalar (String, Int, Boolean, etc.) or another defined type

Directives

  type User {
    id: String @from("user_id") @transform("transformUserId")
    name: String @from("full_name")
    email: String @from("contact_info.email")

    @endpoint(GET, "/users", "data.data[0]")
    @endpoint(POST, "/users", "data.data[0]")
  }
  • @from("api_field_name"): Maps the field to a different name in the API response
  • @transform("transformerName"): Applies a custom transformation to the field
  • @endpoint(METHOD, "path", "dataPath"): Defines REST endpoint for the resource

Query Language

Basic Query Structure

query QueryName {
  resource {
    field1
    field2
    nestedResource {
      nestedField1
    }
  }
}

Query with Arguments

query QueryName($arg: Type) {
  resource(id: $arg) {
    field1
    field2
  }
}

Multiple Resources in One Query

query GetMultipleResources {
  resource1 {
    field1
  }
  resource2 {
    field2
  }
}

Mutation Language

Basic Mutation Structure

mutation MutationName($arg: Type) {
  action(input: $arg) {
    resultField1
    resultField2
  }
}

Example:

mutation CreateUser($name: String!, $email: String!) {
  createUser(name: $name, email: $email) {
    id
    name
    email
  }
}

Nested Data Retrieval

RestQL automatically handles nested data structures:

query GetUserWithPosts {
  user {
    name
    posts {
      title
      comments {
        text
      }
    }
  }
}

Array Fields

Use square brackets to denote array fields:

type User {
  hobbies: [String]
  friends: [User]
}

Nested Arrays:

type User {
  hobbies: [String]
  friends: [[User]]
}

Custom Transformers

Define custom transformers in your RestQL initialization:

const transformers = {
  transformUserId: (originalData, shapedData) => ({
    ...shapedData,
    id: `custom_${shapedData.id}`
  })
};

Execution

Execute queries using the RestQL instance:

const result = await restql.execute(queryString, variables, options);
  • queryString: The RestQL query or mutation
  • variables*: Object containing any variable values
  • options: Additional options like { useCache: true }

Quick Start

import { RestQL } from "lib-restql";

// Define the SDL
const sdl = `
  type User {
    id: String @from("user_id") @transform("transformUserId")
    name: String @from("full_name")
    email: String @from("contact_info.email")
    address: Address @from("location")
    hobbyList: [Hobby] @from("hobbies")
    posts: Post

    @endpoint(GET, "/users", "data.data[0]")
    @endpoint(POST, "/users", "data.data[0]")
    @endpoint(PUT, "/users", "data.data[0]")
    @endpoint(PATCH, "/users", "data.data[0]")
    @endpoint(DELETE, "/users", "data")
  }

  type Post {
    id: String @from("post_id")
    name: String @from("post_name")

    @endpoint(GET, "/posts", "data.data[0]")
    @endpoint(POST, "/posts", "data.data[0]")
    @endpoint(PUT, "/posts", "data.data[0]")
    @endpoint(PATCH, "/posts", "data.data[0]")
    @endpoint(DELETE, "/posts", "data")
  }

  type Address {
    street: String @from("street_name")
    city: String
    country: Country @from("country")
  }

  type Country {
    name: String @from("country_name")
    capital: String @from("capital_name")
    blockList: [[Block]] @from("blocks")
  }

  type Block {
    name: String @from("block_name")
    number: String @from("block_number")
  }

  type Hobby {
    name: String @from("hobby_name")
    id: String @from("hobby_id")
  }
`;

// Define base URLs
const baseUrls = {
  default: "https://api.example.com",
  // "/users": "https://users.api.example.com" // optional
};

// Define options
const options = {
  cacheTimeout: 300000, // 5 minutes
  headers: {
    Authorization: "Bearer your-token-here"
  },
  maxRetries: 3,
  retryDelay: 1000,
  batchInterval: 50
};

// Define transformers
const transformers = {
  transformUserId: (originalData: any, shapedData: any) => {
    return {
      ...shapedData,
      id: `(ID: ${shapedData.id})`
    };
  }
};

// Initialize RestQL
const restql = new RestQL(sdl, baseUrls, options, transformers);

// Define one or many queries
const query = `
  query GetUserWithPosts($userId: String!, $postLimit: Int) {
    user(userId: $userId) {
      id
      name
      email
      parkName
      hobbyList {
        id
        name
      }
      address {
        street
        city
        country {
          name
          capital
          blockList {
            name
            number
            parkList {
              parkName
              parkId
            }
          }
        }
      }
      posts(postLimit: $postLimit) {
        id
        name
        searchResult {
          searchDesc
          searchType
        }
      }
    }
  }
`;

// Execute the query
async function executeQuery() {
  try {
    const result = await restql.execute(query, {
      userId: "123",
      postLimit: 5,
    }, { useCache: true });
    console.log("Query result:", result);

    // Caching
    const resultCached = await restql.execute(query, {}, { useCache: true });
    console.log("Query result cached:", resultCached);
  } catch (error) {
    console.error("Error executing query:", error);
  }
}

executeQuery();

Advanced Usage

Mutations

const batchedMutations = `
  mutation CreateUsers($name: String!, $email: String!) {
    createUser(name: $name, email: $email) {
      id
      name
      email
      address {
        street
        city
        country {
          name
          capital
          blockList {
            name
            number
          }
        }
      }
      hobbyList {
        name
        id
      }
    }
    createUser(name: $name, email: $email) {
      id
      name
      email
    }
  }
`;

async function createUsers() {
  try {
    const result = await restQL.execute(batchedMutations, { name1: 'name_1', emai1: 'email_1', name2: 'name_2', email2: 'email_2' });
    console.log('Created users:', result);
  } catch (error) {
    console.error('Error creating user:', error);
  }
}

Data Mapping Example

RestQL excels at mapping complex REST API responses to the structure defined in your GraphQL-like queries. Here's an example of how RestQL transforms API data:

API Responses

Users API Response:

{
  "data": {
    "data": [
      {
        "user_id": 1,
        "full_name": "test_full_name",
        "contact_info": {
          "email": "[email protected]"
        },
        "location": {
          "street_name": "test_street_name",
          "city": "test_city",
          "country": {
            "country_name": "test_country_name",
            "capital_name": "test_capital",
            "blocks": [
              [{
                "block_name": "test_block_name_1",
                "block_number": "test_block_number_1"
              }, {
                "block_name": "test_block_name_2",
                "block_number": "test_block_number_2"
              }]
            ]
          }
        },
        "hobbies": [
          {
            "hobby_name": "kate",
            "hobby_id": 1
          }
        ]
      }
    ]
  }
}

Posts API Response:

{
  "data": {
    "data": [
      {
        "post_id": 1,
        "post_name": 1
      }
    ]
  }
}

RestQL Query:

query {
  user {
    id
    name
    email
    address {
      street
      city
      country {
        name
        capital
        blockList {
          name
          number
        }
      }
    }
    hobbyList {
      name
      id
    }
  }
  post {
    id
    name
  }
}

Resulting Data Structure:

{
  "post": {
    "id": 1,
    "name": 1
  },
  "user": {
    "id": "idddd_shapedData_1",
    "name": "test_full_name",
    "email": "[email protected]",
    "address": {
      "street": "test_street_name",
      "city": "test_city",
      "country": {
        "name": "test_country_name",
        "capital": "test_capital",
        "blockList": [
          [
            {
              "name": "test_block_name_1",
              "number": "test_block_number_1"
            },
            {
              "name": "test_block_name_2",
              "number": "test_block_number_2"
            }
          ]
        ]
      }
    },
    "hobbyList": [
      {
        "name": "kate",
        "id": 1
      }
    ]
  }
}

Documentation

WIP

Contributing

We welcome contributions! Please see our contributing guidelines for more details.

License

RestQL is MIT licensed. This markdown provides an introduction to your RestQL project, highlighting its key features, providing a quick start guide, and showing some advanced usage examples. You may want to adjust the content based on the specific details of your implementation, add more examples, or include additional sections as needed.

Remember to create the referenced files like CONTRIBUTING.md and LICENSE, and replace link-to-your-docs with the actual link to your documentation if you have one.