• Home
  • Why Erstwhile?
  • Getting Started
  • Learn Erstwhile
  • API Docs
  • Home
  • Why Erstwhile?
  • Getting Started
  • Learn Erstwhile
  • API Docs
  • API Documentation
  • ErstwhileApp
    • getDebug()
    • getConfig(property)
    • setLayout(layout)
    • getComponent(id)
    • getModel(model)
    • redirect(path, updateHistory: true, scrollTop: true)
    • defer(func, delay: 0, selector: null)
    • openModal(controller, modal, args)
    • setModalAttributes(args)
    • closeModal()
  • ErstwhileComponent
    • getCSS()
    • isHidden()
    • hide()
    • show()
    • setProperty(property, value)
    • getProperty(property)
    • isContainer()
    • initialize()
    • unload()
    • getHTML(innerDom)
    • receiveUpdate(key, value)
    • receiveGlobalUpdate(key, value)
    • getTag()
    • getClassName()
    • prepareAttributes()
  • ErstwhileForm
    • getControl(name)
    • showErrors(response)
    • clearErrors()
    • registerComponent(id)
    • deregisterComponent(id)
    • getValues()
  • ErstwhileControl
    • getKey()
    • getValue()
    • setValue(value)
    • setValid(validFlag, message)
  • ErstwhileLayout
  • ErstwhileTheme
    • getRequires()
    • getCSS(appRootDir, workingDir, themeConfig)
    • getFontFolder(appRootDir)
    • getCSSFolder(appRootDir)
    • getJSFolder(appRootDir)
    • getCSSLinks(rootDir)
    • getScripts(rootDir)
  • ErstwhileController
    • preAction(next)
    • postAction(next)
    • getControllerPath()
    • getRoutes()
  • ErstwhileModel
    • makeRequest(path, params, method: "get", data, headers)

API Documentation#

This section of the documentation describes in detail the different classes used in Erstwhile, and goes into detail about how to extend them within your applications.

ErstwhileApp#

ErstwhileApp is the base class that Erstwhile applications built off of. The bootstrap process creates an instance of your subclass of ErstwhileApp, and then makes it available as a variable called $App.

There are a variety of methods in ErstwhileApp that you can use to access your components, models, etc, and also some others to do things like redirect to other pages, or set up functions for deferred execution after the DOM renders. In addition to these, there are a number of undocumented methods that can be used to change the behavior of your application. These won’t be mentioned here because they may change in the future.

getDebug()#

Returns

Value of debug from config

getConfig(property)#

Returns

The specified property from the config.

setLayout(layout)#

Effect

Loads a new layout and places subsequent pages into it.

getComponent(id)#

Returns

The component object with the specified ID. If the component isn’t found, returns false.

getModel(model)#

Returns

The model object with the specified name. If the model isn’t found, returns false.

redirect(path, updateHistory: true, scrollTop: true)#

Arguments
  • path: The path to redirect to.
  • updateHistory: (optional, default true) Whether to add this new path to the history.
  • scrollTop: (optional, default true) Whether the browser should scroll to the top of the page after redirect.
Effects

Sends the user to the specified path. It will run the corresponding controller/action for that path.

defer(func, delay: 0, selector: null)#

Arguments
  • func: The function to defer
  • delay: (optional, default 0) A timeout in millis for how long after DOM rendering you’d like to wait before executing the function
  • selector: (optional, default null) A selector that, if it is present, will call the function immediately. Used in cases in which you don’t know if the selector is present at a given point in time.
Effects

In Erstwhile, the controller actions are executed before the DOM is rendered for the current URL. For this reason, it occasionally happens that you need to defer some code to run until after the DOM is rendered. This is what defer() lets you do.

openModal(controller, modal, args)#

Arguments
  • controller: The controller of the modal
  • modal: The modal to open
  • args: The arguments to pass in to the modal action
Effects

In Erstwhile, modals are implemented similarly to controller actions, only instead of having the method name end with Action in the controller it ends with Modal. They make use of a special scope called modal that lets them have a set of Scoped Variables separate from that of the page that is underneath them.

This function will run the modal’s method and place the view inside the modal, and then show the modal to the user afterwards.

setModalAttributes(args)#

Arguments
  • args: An object that indicates the attributes you’d like to set for the modal. Options include:
    • title: The title of the modal
    • size: The size of the modal (sm, lg, xl)
    • centered: Vertically centers the modal
    • scroll: Sets the modal to be scrollable
    • theme: Sets the color for the modal frame. (Bootstrap colors)
    • buttons: The array of buttons that should be added to the right of Cancel. Each has the following format:
      • label: The label of the button
      • func: The function to run when clicked
      • color: The Bootstrap color to use for the button
Effects

Modals in Erstwhile are implemented with Bootstrap, so this method lets you set the properties of the modal in those terms.

closeModal()#

Effects

This method closes the modal.

ErstwhileComponent#

ErstwhileComponent is the base class for each of the visual (and pseudo-visual) elements in Erstwhile. ErstwhileControl, ErstwhileForm, and ErstwhileLayout are all subclasses of ErstwhileComponent, and each of your custom components should also be a subclass of one of these.

Technically, you don’t need to override any methods in ErstwhileComponent when creating your own components; the default functionality will render the component’s component.ejs file, and if you don’t need anything more than that you’re good to go. You can pick and choose which of these methods to override as you need to change how your component works.

Containers

Some components contain other Erstwhile components (or just plain HTML) that you want to render inside your component. If your component needs this to happen, you can override the isContainer() method and insert a special element <innerContent /> in your component.ejs file to indicate where you would like the contained components/HTML placed.

Changing the component’s rendering behavior

When rendering your component, the ErML engine will, by default, create a <div> element with the component’s id attribute and all of the attributes of your component. If you specify a class attribute on your component, that same class attribute would show up on the <div> when rendered, making it a convenient pass through. The ErML engine will then render your component’s .ejs file and insert that into the <div>.

You have fine-grained control to change this, though.

Overriding getTag(), for instance, lets your component specify a tag other than <div> to use. Overriding prepareAttributes() will let you redefine the behavior for how your component’s attributes turn into HTML attributes.

Moving on to more advanced behavior, you can override the initialize() method to specify Javascript that should run post-DOM render. This will let you instantiate other libraries if needed and direct them to elements in your new DOM.

Finally, it’s sometimes necessary to do something really crazy and implement your own XML structure within your component so consumers can specify different types of data structures not available via simple attributes. If this is you, you can override the getHTML() method. This method has the inner content (as a JSONed XML document) as its argument, and you can freely iterate through that document to fine-tune component behavior.

Scoped Variables

When consuming your components in views, users can specify two different types of attributes:

  • strings, such as value="somevalue"
  • scoped variables, such as value="@.page.value"

From your component’s perspective, both of these are exist in the component’s args member, and can have their present value seen by getProperty(argName). Scoped variables have some bonuses though.

First of all, if the scoped variable is a function, and the attribute it is assigned to is an on* attribute, the scoped function becomes an event handler.

Secondly, if the scoped variable is not a function, your component will receive a callback to receiveUpdate(key, value) every time the scoped variable changes. By overriding this method you will be able to tell your component how to handle that new value.

getCSS()#

Static Function

Returns

String of the CSS needed to add to the bundle for this component.

Effect

If your component would like to add CSS to the bundle separately from what the theme adds, you would specify that here. In the simplest case, you could just return a string with the CSS. If you use a preprocessor, you could also use that here and return the string output.

Note: This method is static and is called at compile time.

isHidden()#

Returns

Boolean indicating whether this item is hidden.

hide()#

Effect

Hides the component by adding the d-none class.

show()#

Effect

Shows the component by removing the d-none class.

setProperty(property, value)#

Effect

Sets the property on your component’s args member to the specified value.

getProperty(property)#

Returns

Gets the specified property on your component’s args member.

isContainer()#

Returns

Boolean indicating whether this component contains inner HTML or other Erstwhile components.

Effect

By default, this returns false. Override it (and return true) to tell the ErML engine to look inside your component’s tag for more HTML/ErML to process.

If your component contains XML that you’d like to further process within your component with a custom getHTML() override, keep this false.

initialize()#

Effect

This method gets called after the DOM for your component is added to the DOM. You override it to instantiate any 3rd party Javascript libraries, initialize component members, or whatever else you need to do to get your component prepared correctly to run.

unload()#

Effect

This method gets called when your component is destroyed, typically when the page it lives on is navigated away from. You can override it to clear any intervals, unregister any event handlers or destroy any variables you’ve initialized to prevent memory leaks.

getHTML(innerDom)#

Arguments
  • innerDom: A fast-xml-parser object representing your component’s inner XML.
Returns

String representing your component’s innerHTML.

Effect

For 95% of components this component can be left as-is; by default it renders your component.ejs file and performs the required updates (checking for the <innerContent /> tag for example) to make your component work.

In special cases, you may wish to override this if your component needs to do something with its child structure. To see this in action, you can check out the Mazer theme’s <DataTable> component.

receiveUpdate(key, value)#

Arguments
  • key: The attribute being updated.
  • value: The new value.
Effect

When one of your components is used with a Scoped Variable assigned to one of the attributes, Erstwhile creates a watch for that variable and will call this method whenever the variable changes elsewhere in the code.

By default it will just update your component’s args member with the new value, but you can override this method to do more advanced behaviors if you would like.

receiveGlobalUpdate(key, value)#

Arguments
  • key: The attribute being updated.
  • value: The new value.
Effect

This method can be ignored unless you override receiveUpdate() in your component. There are a handful of attributes with special meaning in Erstwhile, like value or hidden. Within your custom receiveUpdate() you can call this method at the end to preserve that default behavior.

getTag()#

Returns

String indicating the tag you’d like to use for this component. Default is div

Effect

This method can be overridden if you’d like to use a tag other than div as the wrapper for your component.

getClassName()#

Returns

String indicating the class you’d like to use in the wrapper for your component.

Effect

By default your component will be wrapped in a <div> without a class attribute specified. If you override this method you can set it to something.

prepareAttributes()#

Returns

Object map indicating the attributes you’d like to use for your component’s wrapper.

Effect

By default the ErML engine will pass through any attributes of your component to the wrapper when printing out the HTML. You may wish to change this behavior.

For example, if your component has an attribute called color, you may wish to turn that into a class name (like class="btn-*your color attribute*") when outputting the HTML.

You could affect this by returning the map {"class": "btn-colorname"} (with the colorname being replaced by the value of your component’s color attribute).

ErstwhileForm#

ErstwhileForm is a simple component that basically just exists to provide some convenience functions for dealing with ErstwhileControl subclass components.

It’s actually implemented as an HTML <form> element. Typically you will give it an explicit id attribute to make it easier to reference in the within your code via $App.getComponent().

It is unlikely you will need to subclass it.

getControl(name)#

Arguments
  • name: The key (typically the name attribute) of the ErstwhileControl to return.
Returns

The specified ErstwhileControl object, or false if not found.

Effect

Following the HTML convention, ErstwhileControl components can be referenced via their name attribute. This convenience function makes them easy to look up within a form.

showErrors(response)#

Arguments
  • response: The standard Erswhile response coming back from an API call to an Ersthwhile-compatible backend.
Effect

Erstwhile-compatible backends return an JSON object in this format for Create, Update or Delete types of operations:

{
    "success": , // indicates whether or not the operation was successful
    "authenticated": , // indicates whether or not user has a valid session
    "message": , // optional. Indicates a message to display to the user, either positive or negative.
    "errors": [], // optional. In the case of errors, they may be presented as an array of strings.
    "errorsObj": <object> // optional. Errors mapped to form properties, if needed.
  }

This method takes in this type of response and updates all of the components to show their error states (or not) depending on this structure.

clearErrors()#

Effect

This method clears any form errors, via the Bootstrap standard.

registerComponent(id)#

Arguments
  • id: The the ID of the new ErstwhileControl to register.
Effect

This method is called automatically when an ErstwhileControl is found inside a ErstwhileForm as the ErML engine renders the page. It can also be called manually if you want to add more components to a form. This mostly changes what happens when you call the getValues() method on the form, as that method iterates over the registered controls.

deregisterComponent(id)#

Arguments
  • id: The the ID of the new ErstwhileControl to deregister.
Effect

This method can be called to unregister an ErstwhileControl from an ErstwhileForm. This mostly changes what happens when you call the getValues() method on the form, as that method iterates over the registered controls.

getValues()#

Returns

An object representing the values of all of the controls in this form.

Effect

This method is a convenience function to grab all of the values on the various ErstwhileControl subclasses registered under this form. Remember that ErstwhileControl‘s getValue() method itself returns an object; this method just aggregates these various objects into one.

ErstwhileControl#

ErstwhileControl is the base class for form control components in Erstwhile. These exist mostly to gather input from users.

Following the HTML convention it is recommended that each of these have a name attribute indicating the name to be used within the context of the entity their data represents.

Note: It is important to remember that the getValue() method returns an object rather than a single value, with (by default) the name attribute defining the key and the control’s value as the value. This is to enable more complicated custom controls that may need to return multiple values at once, like a date control that has each of the date parts in separate fields with names like yourname_month, yourname_date, and yourname_year.

getKey()#

Returns

The name of this control, by default the name attribute.

getValue()#

Returns

The value of this control as an object. For most controls this will be in the form { "keyname": value }, but for more advanced controls there may be more items in the object.

setValue(value)#

Arguments
  • value: the value to assign the control.
Effect

This sets the value of a control. Most standard controls would take a simple scalar value but developers are free to choose other structures for their own controls.

setValid(validFlag, message)#

Arguments
  • validFlag: Whether the control’s value is valid.
  • message: A message to display (usually in the case of an error.
Effect

This is a convenience function that is typically called when the control’s parent form is processing a standard Erstwhile response from a Create, Update, or Delete endpoint. If implemented, it will automatically display a control-specific error message within the control to help users fix their data, or clear the error message if the input is valid.

ErstwhileLayout#

ErstwhileLayout is the base class for layouts. In Erstwhile, layouts are essentially the “frame” of the page, the part of the page that stays (generally) consistent as the user moves around the application. This can include the header, navigation, footer, and similar elements to that.

Layouts are implmented just like other ErstwhileComponents, but typically contain a lot of HTML boilerplate due to the nature how web pages are constructed. We call these “Fat Layouts;” Erstwhile lets you place a lot of this non-elegant code in one place and hide it behind a simple component with well-defined integration points in attributes to access its functionality.

For example, the Mazer theme has a ErstwhileLayout component called <FullLayout> that isn’t very pretty:

<%
  function renderSidebarMenuItem( menuItem, submenu = false ) {
    %>
    <li class="<%= (submenu ? "submenu-item" : "sidebar-item") %> <%= (menuItem.id && menuItem.id == args.sidebarMenuActive ? "active" : "") %> <%= (menuItem.children ? "has-sub" : "") %>" <%= (menuItem.id ? `id="sidebar-menu-${menuItem.id}"` : "") %>>
      <a href="<%= (menuItem.link ? menuItem.link : "#") %>" class="<%= (submenu ? "submenu-link" : "sidebar-link") %>">
        <% if(menuItem.biIcon) { %><i class="<%= `bi bi-${menuItem.biIcon}` %>"></i><% } %>
        <span><%= (menuItem.label ? menuItem.label : "Item") %></span>
      </a>
      <% if(menuItem.children) { %>
        <ul class="submenu">
          <% for(let j in menuItem.children) {
            renderSidebarMenuItem(menuItem.children[j], true);
          } %>
        </ul>
      <% } %>
    </li>
    <%
  }

%>
<div id="app">
  <div id="sidebar">
    <div class="sidebar-wrapper active">
      <div class="sidebar-header position-relative">
        <div class="d-flex justify-content-between align-items-center">
          <div class="logo">
            <a href="/"
              ><img
                src="<%- (args.logo ? args.logo : "https://placeholder.pics/svg/500x200") %>"
                alt="Logo"
                srcset=""
            /></a>
          </div>
          
          <div class="sidebar-toggler x">
            <a href="#" class="sidebar-hide d-xl-none d-block"
              ><i class="bi bi-x bi-middle"></i
            ></a>
          </div>
        </div>
      </div>
      <div class="sidebar-menu">
        <ul class="menu">
          <% if(args.sidebarMenu && args.sidebarMenu.length > 0) { %>
            <% for(let i in args.sidebarMenu) {
              if(args.sidebarMenu[i].label) { %><li class="sidebar-title"><%= args.sidebarMenu[i].label %></li><% } %>
            <% 
              for(let j = 0; j < args.sidebarMenu[i].items.length; j++) { 
                renderSidebarMenuItem(args.sidebarMenu[i].items[j]);
              } 
            %>
          <% }} %>
        </ul>
      </div>
    </div>
  </div>
  <div id="main">
    <header class="mb-3">
      <a href="#" class="burger-btn d-block d-xl-none">
        <i class="bi bi-justify fs-3"></i>
      </a>
    </header>

    <div class="page-heading">
      <div class="page-title">
        <div class="row">
          <div class="col-12 col-md-6 order-md-1 order-last">
            <h3 id="page-title"><%- args.pageTitle %></h3>
            <p id="page-intro" class="text-subtitle text-muted">
              <%- args.pageIntro %>
            </p>
          </div>
          <div class="col-12 col-md-6 order-md-2 order-first">
              <nav
                aria-label="breadcrumb"
                class="breadcrumb-header float-start float-lg-end"
                id="breadcrumb-container"
              ><% if(args.breadcrumbs) { %>
                <ol class="breadcrumb">
                  <% for(let i in args.breadcrumbs) { %>
                    <li class="breadcrumb-item <%= (i == args.breadcrumbs.length - 1 ? 'active' : "") %>" <%= (i == args.breadcrumbs.length - 1 ? 'aria-current="page"' : "") %>>
                      <% if(args.breadcrumbs[i].link) { %>
                        <a href="<%- args.breadcrumbs[i].link %>"><%- args.breadcrumbs[i].label %></a>
                      <% } else { %>
                        <%- args.breadcrumbs[i].label %>  
                      <% } %>
                    </li>
                  <% } %>
                </ol>
                <% } %>
              </nav>
            
          </div>
        </div>
      </div>
      <pagecontent />
    </div>

    <footer>
      <div class="footer clearfix mb-0 text-muted">
        <div class="float-start">
          <% if(args.copyrightYear || args.copyrightName) {%>
            <p><%- args.copyrightYear %> &copy; <%- args.copyrightName %></p>
          <% } %>
          
        </div>
        <div class="float-end">
          <% if (args.footerMessage) { %>
            <p>
              <%- args.footerMessage %>
            </p>
          <% } %>
          
        </div>
      </div>
    </footer>
  </div>
</div>

Should you choose to include it within your application, though, you can consume it like this:

<FullLayout 
  logo="/assets/images/todos-logo.png" 
  sidebarMenu="@.session.sidebarMenu"
  sidebarMenuActive="@.page.sidebarMenuActive"
  breadcrumbs="@.page.breadcrumbs"
  copyrightYear="<%= (new Date()).getFullYear() %>"
  copyrightName="<a class='external' target='_blank' href='https://www.restlessdev.com'>RestlessDev</a>"
  footerMessage="Made with <span class='text-danger'><i class='bi bi-heart-fill icon-mid'></i></span> in New Orleans"
  pageTitle="@.page.title"
  pageIntro="@.page.intro"
  appName="Todos" 
  meta="@.page.meta"
  >
  <pagecontent />
</FullLayout>

You can ignore the unsightly parts of the code and just work with the wrapper.

At this time, ErstwhileLayout doesn’t have any specific methods that can be overwritten. When creating layouts, you can denote the place you’d like to insert the page content with a  <pagecontent /> tag.

ErstwhileTheme#

The ErstwhileTheme is the base class for themes in Erstwhile. When making a new theme, you create a subclass of this class and place it a file called theme.js in the root of your theme’s directory.

This class is used at compile time by the Erstwhile compiler to integrate your theme into the Erstwhile application and satisfy all theme dependencies. Consequently, most of the methods in this class are meant to be static.

getRequires()#

Static Function

Returns

An array of strings indicating the required dependencies of this theme.

Effect

This method returns an array of strings representing the packages this theme depends on. The compiler will then attempt to throw errors if these dependencies aren’t met.

Note: This method is static and is called at compile time.

getCSS(appRootDir, workingDir, themeConfig)#

Static Function

Arguments
  • appRootDir: The full directory being used to compile the app.
  • workingDir: The full working directory being used to compile the app. Typically /build within the app root.
  • themeConfig: Any theme-specific settings in the app.js config file under the themeConfig key.
Returns

String of the CSS needed to add to the bundle for this theme.

Effect

If your theme would like to add CSS to the bundle separately from what the individual components add, you would specify that here.

While it is still the early days of Erstwhile theme creation, it is quite common for general-purpose HTML themes to be adapted to work with Erstwhile. Most of these have their CSS generated through a preprocessor like SCSS. For these cases, this SCSS can be added to the theme’s /theme directory and then build within the working directory specified in the second argument at compile time.

The third argument lets you add custom configuration variables for your theme that a user could add to their application’s config file; for example, you may want to give theme a simple way to specify primary and secondary colors, or font sizes.

As this method does all of the compilation work and simply returns a (potentially giant) CSS string, you have a lot of latitude with how it does its work. Please see the sample Mazer theme for some ideas.

Note: This method is static and is called at compile time.

getFontFolder(appRootDir)#

Static Function

Arguments
  • appRootDir: The full directory being used to compile the app.
Returns

The directory path containing your theme’s font files.

Effect

If your theme has fonts that need to be brought into the application, you can specify the directory here and they will be copied to /dist/assets/fonts when the application is compiled.

getCSSFolder(appRootDir)#

Static Function

Arguments
  • appRootDir: The full directory being used to compile the app.
Returns

The directory path containing your theme’s additional CSS files.

Effect

If your theme has additional CSS files (from plugins used, for example) that need to be brought into the application, you can specify the directory here and they will be copied to /dist/assets/css when the application is compiled.

getJSFolder(appRootDir)#

Static Function

Arguments
  • appRootDir: The full directory being used to compile the app.
Returns

The directory path containing your theme’s additional JS files.

Effect

If your theme has additional JS files (from plugins used, for example) that need to be brought into the application, you can specify the directory here and they will be copied to /dist/assets/js when the application is compiled.

getCSSLinks(rootDir)#

Static Function

Arguments
  • rootDir: The relative URL of the CSS folders from the web root.
Returns

A string containing all of the additional <link /> tags needed to bring in your theme’s additional CSS files.

Effect

If you’ve added additional CSS files to your theme (for included plugins, for example) you can specify the specific links needed to bring those files into your application.

getScripts(rootDir)#

Static Function

Arguments
  • rootDir: The relative URL of the CSS folders from the web root.
Returns

A string containing all of the additional <script /> tags needed to bring in your theme’s additional JS files.

Effect

If you’ve added additional JS files to your theme (for included plugins, for example) you can specify the specific <script> tags needed to bring those files into your application.

ErstwhileController#

ErstwhileController is the base class for themes in Erstwhile. It doesn’t have many specific methods to be overridden, but it does have certain conventions to follow when using it.

There are two primary types of methods specified in controllers:

  • Actions: By the default Erstwhile routing behavior, these each get turned into routes and are accessed via URLs like /<controllerName>/<actionName>. Actions always have method names ending with Action.
  • Modals: Modals are similar to Actions, but are triggered when calling the application’s openModal() method. They have a special scope called modal that can store variables outside of session and page. Modals always have method names ending in Modal.

There are also two reserved methods that can be overreidden called preAction() and postAction(), which are called as part of the request lifecycle.

preAction(next)#

Arguments
  • next: A function to run on success.
Effect

This method is called automatically by the controller before performing the requested action. It can be used to initialize anything needed, check that the user is authenticated, etc. Importantly, on success the developer must call the next() function passed in as an argument to continue execution. If you would like to divert the user down another path (because they failed authentication, for example) you could redirect them to the login page instead.

postAction(next)#

Arguments
  • next: A function to run on success.
Effect

This method is called automatically by the controller after performing the requested action. It can be used to perform cleanup after the request, if needed. Importantly, on success the developer must call the next() function passed in as an argument to continue execution.

getControllerPath()#

Return

Either false or a string you’d like to use for this controller when using the routing table. The default is the portion of the controller name ahead of the Controller.

Effect

This lets you change how the routing table is created for this controller.

getRoutes()#

Return

Either false to use the default routing behavior, or an object of paths and the actions you would like to map them too.

  {
    "/login": {
      "action": "login"
    },
    "/logout": {
      "action": "logout"
    }
  }
Effect

Overriding this method lets one change the default routing behavior of your controllers, if needed. You can specify any path in the key, and denote wildcards with :id syntax. Each of these whildcards will be passed in the the args array of your action if the path is matched.

ErstwhileModel#

ErstwhileModel is the base class for models in Erstwhile.

Of the different parts of the MVC framework, the Model layer is the most flexible. The only strict requirement with models is that they are exported by class name in the file /app/models/model.js, and that any methods on them are called statically. How you store data internally to the models isn’t important to Erstwhile.

If you choose to do things The Erstwhile Way™ you’ll receive some benefits, though.

Typically with web applications, the model layer is used to interact with backend systems. This involves hitting various API endpoints on a server to fetch and update data, authenticate, etc. To ease this process, Erstwhile uses a configuration file to describe the endpoints available to it, structures of entities coming out of the server, as well as certain metadata about how authentication happens.

This file is called description.json.

When working with an Erstwhile-aware backend, this file is published by the server at a known endpoint (during development. It isn’t necessary once the application is in production) and the framework is able to fetch this file and build out its model layer through a command line command. As the API changes, the people maintaining it will keep description.json up to date with the changes, and tell you to rebuild your model when needed.

If you are running an ExpressJS-based backend, there is an npm package called erstwhile-backend you can use to help you get up and running with publishing your description.json. It also includes a handy HTML output version so that you can point developers consuming your API to constantly-updated documentation on what your API offers.

If you don’t have an Erstwhile-aware backend, you can create your own description.json and store it in your /app/config directory, and the model build script will use that instead. To get started, we have a handy description.json builder.

makeRequest(path, params, method: "get", data, headers)#

Static Function

Arguments
  • path: The relative path of the endpoint. If this is parameterized, the parameters appear in the form :id.
  • parameters: An object with the parameters of the request. These get added to the URL by the library.
  • method: The HTTP method.
  • data: The data of the request. Could be a JSON body or an object with the query string properties for the request.
  • headers: Any additional headers to set.
Returns

An axios promise with the request.

Effect

This method is used internally by the library to call the various methods derived from the description.json file. It doesn’t do any validation of either the request or response, and assumes the server will take care of that.

loader

Join our Mailing List     

  • Privacy Policy
  • Terms of Use
  • © 2025 RestlessDev. Made in New Orleans.

Welcome to Erstwhile

Thank you for checking out Erstwhile. We’re just getting started, so if you’d like to keep up with our progress please register now and we’ll let you know when new releases come out.

loader

Email Address*

Name