/walder

Walder offers an easy way to set up a website or Web API on top of decentralized knowledge graphs.

Primary LanguageJavaScriptMIT LicenseMIT

logo of Walder

Walder offers an easy way to set up a website or Web API on top of decentralized knowledge graphs. Knowledge graphs incorporate data together with the meaning of that data. This makes it possible to combine data from multiple knowledge graphs, even if different, independent parties maintain or host them. Knowledge graphs can be hosted via Solid PODs, SPARQL endpoints, Triple Pattern Fragments interfaces, RDF files, and so on. Using content negotiation, Walder makes the data in these knowledge graphs available to clients via HTML, RDF, and JSON-LD. Users define in a configuration file which data Walder uses and how it processes this data. Find out which APIs are built with Walder here.

Table of contents

Installation

Install Walder globally via yarn global add walder.

For development, follow these steps:

  1. Clone the repository.
  2. Install dependencies via yarn install.
  3. Install Walder globally via $ yarn global add file:$(pwd).

Usage

Walder is available as a CLI and JavaScript library.

CLI

Usage: walder [options]

Options:
  -v, --version              output the version number
  -c, --config <configFile>  YAML configuration file input
  -p, --port [portNumber]    server port number (default: 3000)
  -l, --log [level]          enable logging and set logging level (one of [error, warn, info, verbose, debug]) (default: "info")
  --no-cache                 disable Comunica default caching
  -h, --help                 output usage information

Library

// From the root directory
const Walder = require('.');

const configFilePath = 'path/to/configfile';
const port = 9000; // Defaults to 3000 
const logging = 'info'; // Defaults to 'info' 
const cache = false;  // Defaults to true 

const walder = new Walder(configFilePath, {port, logging, cache, cwd});

walder.activate();    // Starts the server
walder.deactivate();  // Stops the server

Config file structure

You write a config file in YAML following the OpenAPI 3.0 specification. The config file must have the following structure:

openapi: 3.0.2
info:  # OpenAPI metadata
  title: 'Example site'
  version: 0.1.0
x-walder-resources:  # Directories used by Walder - OPTIONAL
  root:  # Path to the root folder of the directories used by Walder (absolute or relative to the directory containing the config file) - OPTIONAL (default: .)
  views:  # Path to directory containing template (view) files (absolute or relative to the root folder) - OPTIONAL (default: views)
  pipe-modules:  # Path to directory containing local pipe modules (absolute or relative to the root folder) - OPTIONAL (default: pipe-modules)
  public:  # Path to directory containing all files that should be available statically (e.g. stylesheets) (absolute or relative to the root folder) - OPTIONAL (default: public)
  layouts: # Path to directory containing all files that can be used by template files (absolute or relative to the root folder) - OPTIONAL (default: layouts)
x-walder-datasources:  # Default list of data sources
  - ...  # E.g. link to SPARQL endpoint or a GraphQL-LD query
paths:  # List of path entries
  path-entry-1:
    ...
  path-entry-2:
    ...
x-walder-errors: # Default error page views - status codes with files containing the HTML template (absolute path or relative to the views directory)
  404: ...
  500: ...
  ...

Resources

The x-walder-resources key of the config file contains paths to directories used by Walder. This key and it's values are optional. If a user does not give paths, Walder uses the following default values relative to the directory of the config file.

root: .
views: views
pipe-modules: pipe-modules
public: public
layouts: layouts

To prevent Walder from making the wrong files public, when a user does not give a path to the public field, Walder creates a new directory public if it does not find this directory in the current working directory and uses that one.

Path entry

A path entry defines a route and has the following structure:

path:  # The path linked to this query
  request:  # The HTTP request type (GET, POST, etc.)
    summary: ...  # Short description
     parameters:  # Path variables/Query parameters
        - in: ...  # 'path' or 'query'
          name: ...  # Name of the parameter
          schema:
            type: ... # Type of the parameter
          description: ...  # Description of the parameter
    x-walder-query:
      graphql-query: ...  # One or more GraphQL queries
      json-ld-context: ...  # The JSON-LD corresponding to the GraphQL query
      options: # Global options that will be applied to all the graphql-queries of this path (OPTIONAL)
      datasources:  # Query specific datasources (OPTIONAL)
        additional: ...  # Boolean stating that the following datasources are meant to be used on top of the default ones
        sources:  # List of query specific datasources
          - ...  # E.g. link to SPARQL endpoint
    x-walder-postprocessing:  # The (list of) pipe modules used for postprocessing
      module-id:  # Identifier of the pipe module
        source: ...  # Path leading to source code of the pipe module (absolute path or relative to the pipe-modules directory)
        parameters: # the parameters for the pipe module (OPTIONAL)
          - _data # (DEFAULT) this gives all the data, but all paths in the data object are supported (e.g. _data.0.employee)
          - ... # Additional parameters if you're function supports those (OPTIONAL)
    responses:  # Status codes with files containing the HTML template (absolute path or relative to the views directory)
      200: ...  # (REQUIRED)
      500: ...  # (OPTIONAL)

Example

The following command starts a server on port 3000 (default) using an example config file.

$ walder -c example/config.yaml

This will start a server on localhost:3000 with the following routes:

Options

In the path entry above, the user defined options as a global (optional) identifier that Walder uses for every query of that path. We have two options where we can choose from: sort and remove-duplicates. With given syntax:

options:
  sort: # Enable sorting on the data (OPTIONAL)
    object: # JSONPath to the object you want to sort for
    selectors: # The values inside the object over which you want to sort
      - ... # The default option when you want ascending order, just give the value (JSONPath notation supported for further nesting)
      - value: ...  # When you want descending order, specify the value/order (JSONPath notation supported for further nesting)
        order: desc
  remove-duplicates: # Enable the removal of duplicates of the data (OPTIONAL)
    object: ... # The JSONPath tot the object that you want to compare
    value: ... # The value that has to be compared to determine whether it's duplicate (JSONPath notation is also supported for further nesting)

If you do not want options to be global for the whole path, you can define options per query.

path:  # The path linked to this query
  request:  # The HTTP request type (GET, POST, etc.)
    summary: ...  # Short description
     parameters:  # Path variables/Query parameters
        - in: ...  # 'path' or 'query'
          name: ...  # Name of the parameter
          schema:
            type: ... # Type of the parameter
          description: ...  # Description of the parameter
    x-walder-query:
      graphql-query: ...  # One or more GraphQL queries
        name:
          query: ... # The GraphQL query
          options: # options that will be applied only to this specific graphql-query (OPTIONAL)
...

The following command starts a server using this config file.

$ walder -c example/config-sorting-duplicates.yaml

This will start a server on localhost:3000 with the following routes:

Multiple config files

You can split a config file in multiple files, using the $ref keyword. We follow the OpenAPI 3.0 spec that explains how to use the referencing.

When first referenced you need to use the path beginning from the directory of the config file, but if the referenced file has references itself, it can use paths relative to its own location, as shown below.

The actual config file referencing its paths

openapi: 3.0.2
info:
  title: 'Example site'
  version: 0.1.0
x-walder-resources:
  path: ./
  views: views
  pipe-modules: pipeModules
  public: public
x-walder-datasources:
  - http://fragments.dbpedia.org/2016-04/en
paths:
  /music/{musician}:
    $ref: './paths/music_musician.yaml'
  /movies/{actor}:
    $ref: './paths/movies_actor.yaml'
x-walder-errors:
  404:
    description: page not found error
    x-walder-input-text/html: error404.html
  500:
    description: internal server error
    x-walder-input-text/html: error500.html

Below you see ./example/paths/music_musician.yaml with reference with path relative to its own location

get:
  summary: Returns a list of the all movies the given actor stars in
  parameters:
    - in: path
      name: actor
      required: true
      schema:
        type: string
      description: The target actor
  x-walder-query:
    $ref: '../walderQueryInfo/movies_actor_info.yaml'
  responses:
    200:
      description: list of movies
      x-walder-input-text/html: movies.pug

Content negotiation

Using content negotiation, Walder makes the following output formats available:

  • 'text/html'
  • 'application/ld+json'
  • 'text/turtle'
  • 'application/n-triples'
  • 'application/n-quads'

RDF

Since Walder uses graphql-ld-comunica to execute the GraphQL queries, which return JSON data, Walder first converts it into JSON-LD. This enables easy conversion to other RDF formats.

HTML templates

Walder uses consolidate to automatically retrieve the corresponding engine for a given template. This means that the supported template engines are dependent on consolidate.

You can use different template engines for different routes, e.g., pug renders one route's HTML, while handlebars renders another route's HTML. Walder does this all by looking at the file extension of the given template.

Input validation

While parsing the config file, Walder also validates the correctness and completeness of the input. When Walder has parsed the whole config file and found errors, Walder returns all errors and exits.

At the moment, Walder validates the following:

  • The config files describes all variables in the GraphQL-LD query in the parameters section.

Error handling

Walder binds error pages to a certain HTTP status code. You can define default error pages, but also path specific error pages by adding them to the responses key in the corresponding path entry.

Errors

Global

  • Error 404: Page not found
  • Error 500: Internal server error

Pipe modules

  • Error 500: Could not apply the given pipe modules

GraphQL-LD

  • Error 404: Expected variable was not given
  • Error 500: Could not execute the given query

Example

When you run Walder using the following command:

$ walder -c example/config-errors.yaml

the following paths lead to errors:

The following config file excerpt will use the path specific moviesServerError.handlebars template on errors leading to status code 500 when navigating to /movies.

When the required query parameter actor is not passed, Walder returns the status code 404. Walder will use the default error404.html file since the config file has no path-specific HTML template for the corresponding status.

...
paths:
  /movies:
    get:
      summary: Returns a list of the all movies the given actor stars in
      parameters:
        - in: query
          name: actor
          schema:
            type: string
            minimum: 0
          description: The actor from whom the movies are requested
          required: true
      x-walder-query:
        graphql-query: >
          {
            id @single
            ... on Film {
              starring(label: $actor) @single
            }
          }
        json-ld-context: >
          {
            "@context": {
              "Film": "http://dbpedia.org/ontology/Film",
              "label": { "@id": "http://www.w3.org/2000/01/rdf-schema#label", "@language": "en" },
              "starring": "http://dbpedia.org/ontology/starring"
            }
          }
      responses:
        200:
          description: list of movies
          x-walder-input-text/html: movies.pug
        500:
          description: internal movie server error
          x-walder-input-text/html: moviesServerError.handlebars
x-walder-errors:
  404:
    description: page not found error
    x-walder-input-text/html: error404.html
  500:
    description: internal server error
    x-walder-input-text/html: error500.html

Dependencies

Library License
@comunica MIT
accepts MIT
axios MIT
chai MIT
commander MIT
consolidate MIT
debug MIT
express MIT
front-matter MIT
fs-extra MIT
graphql-ld MIT
graphql-ld-comunica MIT
handlebars MIT
is-html MIT
jade-to-handlebars MIT
json-refs MIT
jsonld BSD-3-Clause
jsonpath MIT
markdown-it MIT
mocha MIT
morgan MIT
n3 MIT
object-path MIT
pug MIT
supertest MIT
tmp MIT
winston MIT
yaml ISC

Tests

Built with Walder

Did you build something Walder and want to add it to the list? Please create a pull request!

License

This code is copyrighted ©2019–2020 by Ghent University – imec and released under the MIT license.