SkillAgentSearch skills...

Crudl

CRUDL is a backend agnostic REST and GraphQL based admin interface

Install / Use

/learn @seeekr/Crudl
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

crudl

CRUDL is a React application for rapidly building an admin interface based on your API. You just need to define the endpoints and a visual representation in order to get a full-blown UI for managing your data.

TOC

Architecture

The CRUDL architecture (depicted below) consists of three logical layers. The connectors, views, and the react-redux frontend. We use React and Redux for the frontend, which consists of different views such as list, add, and change view. The purpose of the connectors layer is to provide the views with a unified access to different APIs like REST or GraphQL. You configure the connectors, the fileds, and the views by providing a admin.

+-----------------------+
|     React / Redux     |     
+-----------------------+
|         Views         |
+-----------------------+
  ↓         ↑         ↑         CRUDL
request  response  errors
  ↓         ↑         ↑
+-----------------------+
|       Connectors      |
+-----------------------+       ------------
            ↕                  
         ~~~~~~~           
           API                  BACKEND
         ~~~~~~~             

Admin

The purpose of the admin is to provide CRUDL with the necessary information about the connectors and the views. The admin is an object with the following attributes and properties:

const admin = {
    title,              // Title of the CRUDL instance (a string or a react element property)
    connectors,         // an array of connectors
    views,              // a dictionary of views
    auth: {
        login,          // Login view descriptor
        logout,         // Logout view descriptor
    },
    custom: {
        dashboard,      // The index page of the CRUDL instance (a string or a react element property)
        pageNotFound,   // The admin of the 404 page        
        menu,           // The custom navigation
    },
    options: {
        debug,          // Include DevTools (default false)
        basePath,       // The basePath of the front end (default  '/crudl/')
        baseURL,        // The baseURL of the API backend (default  '/api/')
        rootElementId,  // Where to place the root react element (default 'crudl-root')
    }
}

The provided admin will be validated (using Joi) and all its attributes and properties are checked against the admin's schema.

Attributes and properties

We distinguish between attributes and properties. An attribute is a value of a certain type (such as string, boolean, function, an object, etc.), whereas property can also be a function that returns such a value. In other words, with property you can also provide the getter method. For example, the title of the CRUDL instance is a string (or react element) property. So you can define it as

title: 'Welcome to CRUDL'`

or as

title: () => `Welcome to CRUDL. Today is ${getDayName()}

or even as:

title: () => <span>Welcome to <strong>CRUDL</strong>. Today is {getDayName()}</span>,

Options

In admin.options you may specify some general CURDL settings

{
    debug: false,                   // Include DevTools?
    basePath: '/crudl/',            // The basePath of the front end
    baseURL: '/api/',               // The baseURL of the API (backend)
    rootElementId: 'crudl-root',    // Where to place the root react element
}

Assuming we deploy CRUDL on www.mydomain.com, we'll have CRUDL running on www.mydomain.com/crudl/... and the ajax requests of the connectors will be directed at www.mydomain.com/api/....

Connectors

The purpose of the connectors is to provide CRUDL with a unified view of the backend API. A connector is an object that defines the four CRUD methods create, read, update, and delete. These methods accept a request object as their argument and return a promise that either resolves to a response object or throws an error. Normally, a single connector represents a single API endpoint or a single resource. So you define, for example, a single connector to access the blog entries and another connector to access the users.

CRUDL provides connectors for RESTful and GraphQL APIs. A REST connector must define the url attribute and a GraphQL connector must define the query attribute.

A connector has the following schema:

{
    id,             // A string uniquely identifying the connector
    url,            // REST: The endpoint URL (will be appended to options.baseURL)
    urlQuery,       // REST: A function that builds the url query part
    query,          // GraphQL: The GraphQL queries for create, read, update, and delete operations
    mapping,        // The mapping between CRUD and HTTP methods
    transform,      // Definition of Request and Response transformations
    pagination,     // Function that returns pagination info
    baseURL,        // Overrides the value of admin.options.baseURL for this particular connector
}
  • url: url can either be a string such as users/, that will resolve against the baseURL option. Or it can be a function of the form: (request) => urlString

  • urlQuery: is an optional attribute. When provided, it must be a function (request) => query, where query is an object of url query keys and values e.g. { search: 'John', sortBy: 'last_name' }. The resulting URL would then be: baseURL/users?search=John&sortBy=last_name.

  • query: An object with attributes create, read, update, and delete each defining a GraphQL query. The definition of the GraphQL query can be either a string or a function (request) => queryString

  • mapping: An object that defines the mapping between the CRUD and HTTP methods. The default mapping of a REST connector is:

    {
        create: 'post',
        read: 'get',
        update: 'patch',
        delete: 'delete',
    }
    

    The default mapping of a GraphQL admin is:

    {
        create: 'post',
        read: 'post',
        update: 'post',
        delete: 'post',
    }
    
  • transform: An object of request and response transformations:

    {
        // Request
        createRequest: (req) => req,
        readRequest: (req) => req,
        updateRequest: (req) => req,
        deleteRequest: (req) => req,
        // Request data
        createRequestData: (data) => data,
        readRequestData: (data) => data,
        updateRequestData: (data) => data,
        deleteRequestData: (data) => data,
        // Response
        createResponse: (res) => res,
        readResponse: (res) => res,
        updateResponse: (res) => res,
        deleteResponse: (res) => res,
        // Response data
        createResponseData: (data) => data,
        readResponseData: (data) => data,
        updateResponseData: (data) => data,
        deleteResponseData: (data) => data,
    }
    

    The transformation of a request is applied prior to the transformation of request data and similarly, the transformation of a response is applied prior to transformation of a response data.

  • pagination: a function (response) => paginationInfo, where the format of paginationInfo depends on the kind of pagination that is being used.

    The numbered pagination requires pagination info in the form: { allPages, currentPage, resultsTotal, filteredTotal }, where allPages is an array of page cursors. Page cursors can be any data. allPages[i-1] must provide a page cursor for the i-th page. The currentPage is the page number of the currently displayed page. The corresponding page cursor of the current page is allPages[currentPage-1]. The total number of results can be optionally provided as resultsTotal. The total number of filtered results can be optionally provided as filteredTotal.

    The continuous scroll pagination requires the pagination info in the form: { next, resultsTotal, resultsTotal, filteredTotal }. Where next is a pageCursor that must be truthy if there exist a next page, otherwise it must be falsy. The resultsTotal is optional and it gives the number of the total available results. The total number of filtered results can be optionally provided as filteredTotal.

  • baseURL: A string that overrides the admin.options.baseURL value for this particular connector. It allows to access different API at different base URLs.

Bare Connectors

If neither url nor query are provided, then the connector is called a bare connector and it must provide the CRUD methods directly, for example like this:

{
    // Provide some testing data
    read: () => Promise.resolve({
        data: require('./testdata/tags.json')
    }),
    // Pretend to create a resource
    create: (req) => Promise.resolve({
        data: req.data
    }),
},

Requests

A request object contains all the information necessary to execute one of the CRUD methods on a connector. It is an object with the following attributes:

{
    data,           // Context dependent: in a change view, the data contains the form values
    params,         // Connectors may require parameters to do their job, these are stored here
View on GitHub
GitHub Stars4
CategoryDevelopment
Updated2y ago
Forks22

Languages

JavaScript

Security Score

70/100

Audited on Feb 21, 2024

No findings