wu

Reactive, functional and declarative framework for webapps

View the Project on GitHub migueldelmazo/wu

Wu framework: API documentation

API allows you to watch changes in the data model and send Ajax requests to your server. When the server responses, API helps you manage it and save it in the data model.

Pattern

Data flow:

Reactive data model ➤ API (pure functions) ➤ Server ➤ API (pure functions) ➤ Reactive data model.

The flow that Wu performs with each API item is:

API definition properties:

| Properties | Required | |:—————————————————-:|:——–:| | onChange | Required | | when | Optional | | request | Required | | onResponse | Required | | options | Optional |

Example:

wu.create('api', 'getUserProfile', {
  onChange: 'user.id',
  request: {
    method: 'get',
    path: 'https://server.com/api/user/profile',
    query: {
      userId: 'user.id'
    }
  },
  onResponse: {
    status200: {
      run: (response) => response.body,
      update: 'user.profile'
    }
  },
  options: {
    cacheable: false
  }
})

Request property:

The definition of the request is formed by:

Each of these 5 properties can be:

wu.create('api', 'getUserProfile', {
  onChange: 'user.id',
  request: {
    // 'path' is not modified because is a string
    path: 'https://server.com/api/user/profile',
    // Wu get 'args' from data model and run this function with this args to get 'query'
    query: {
      args: 'user.id',
      run: (userId) => {
        return {
          userId: userId
        }
      }
    },
    // Wu parses 'headers' with data model through 'wu.model.populate()' method
    headers: {
      authentication: 'user.authentication.token'
    }
  },
  onResponse: {}
})

onResponse property:

See options.handler to customize the handlers.

wu.create('api', 'sendUserLogin', {
  onChange: 'user.id',
  request: {
    method: 'post',
    path: 'https://server.com/api/user/login',
    body: {
      email: 'user.email',
      password: 'user.password'
    }
  },
  onResponse: {
    // array handler, it always runs
    init: [
      {
        run: (response, request, options) => response.status,
        update: 'login.call.status'
      },
      {
        run: (response, request, options) => response.errorMessage,
        update: 'login.call.errorMessage'
      }
    ],
    // custom handler, it runs if the server returns a 200 status
    status200: {
      run: (response, request, options) => response.body,
      update: 'user.login.data'
    },
    // custom handler, it runs if the server returns a 404 status
    status404: {
      run: (response, request, options) => '',
      update: 'user.password'
    },
    // handler with data model args, it runs if the server responds correctly
    success: {
      args: 'user.name',
      run: (userName, response, request, options) => 'Welcome ' + userName,
      update: 'user.login.greeting'
    },
    // it runs if the server fails
    error: {
      run: (response, request, options) => 'Fatal error',
      update: 'app.message'
    },
    // it always runs
    complete: {
      run: (response, request, options) => true,
      update: 'login.call.isCompleted'
    }
  }
})

Options property:

You can add the following options to the definition of your API:

options.cacheable:

Wu only caches the calls with GET method, 200 status code and without errors. If you do not want Wu to cache a call add the property options.cacheable = false to your API item.

wu.create('api', 'sendUserLogin', {
  onChange: 'user.id',
  request: {
    path: 'https://server.com/api/user/profile',
  },
  onResponse: {
    success: {
      run: (response) => response.body,
      update: 'user.data'
    }
  },
  options: {
    cacheable: false
  }
})

options.flags:

A simple way to know if a call is sending is to add flags to an API element. The same to know if the call went well, there were errors, what state code was returned…

To do this, indicate the path of the data model where you want each of these flags to be updated:

wu.create('api', 'sendUserLogin', {
  onChange: 'user.id',
  request: {
    path: 'https://server.com/api/user/profile',
  },
  onResponse: {
    success: {
      run: (response) => response.body,
      update: 'user.data'
    }
  },
  options: {
    flags: {
      sending: 'user.login.sending',
      success: 'user.login.success',
      error: 'user.login.error',
      complete: 'user.login.complete',
      errorMessage: 'user.login.errorMessage',
      status: 'user.login.status'
    }
  }
})

options.context:

The same API item can be used to send calls to the server with different data or to send several calls in parallel.

To execute each of these calls with different values use options.context which is an object that can have the properties when and run. The run function must return an array. A call will be sent to the server with each element of the array.

The request properties (method, path, body, query and headers) receive each of these elements as the last argument.

The onResponse handlers (init, success, error, complete, status200…) receive options.context as the last argument.

wu.create('api', 'getUsers', {
  onChange: 'users',
  request: {
    // This call is executed once for each of the elements that are in 'users'
    path: {
      // options = { context: { user: {...} } }
      run: (options) => 'https://server.com/api/user/' + options.context.id
    }
  },
  onResponse: {
    success: {
      args: ['users'],
      // options = { context: { user: {...} } }
      run: (users = [], response, request, options) => {
        return users.map((user) => {
          if (user.id === options.context.id) {
            user.data = response.body
          }
          return user
        })
      },
      update: 'users'
    }
  },
  options: {
    // return an array
    context: {
      args: ['users'],
      run: (users) => users
    }
  }
})

options.handler:

If you want to change the custom handler you can do it by adding the object options.handler. This object can have the properties args and run. And also receives as last arguments response, request and options.

options.handler must return a string.

wu.create('api', 'sendUserLogin', {
  onChange: 'user.id',
  request: {
    path: 'https://server.com/api/user/profile',
  },
  onResponse: {
    ok: {
      run: (response) => response.body,
      update: 'user.data'
    },
    ko: {
      run: (response) => ({}),
      update: 'user.data'
    }
  },
  options: {
    handler: {
      run: (response, request, options) => {
        return response.status === 200 ? 'ok' : 'ko'
      }
    }
  }
})

Wu listens to the property window.navigator.onLine and updates api.online property of data model each time your browser loses or recovers the Internet connection. ___

Queue:

Calls to the server go through a queue before they are sent.

Wu is a preventive system. If you make several calls in parallel through the same API item and with the same request values, only the first one will be sent to the server. The rest will be ignored.