# Startupjs application file structure (application entry point, backend, standalone applications)

After running `npx startupjs init myapp` you will get a folder with the `myapp` project. If you open it, you will see a specific file structure, which should be examined in further detail.

## Naming files

Since startupjs has `react-native` in its core, each file is interpreted as a file for compilation for both mobile device and web version. Still, it is possible to split files based on the platform. To do this, use the following principle:

  * `*.web.js` for files, only suited for the web version.
  * `*.js` for files, suited for mobile devices.

If there is no file named `*.web.js`, then the `*.js` file is compiled for both web and mobile version.

## Why this is needed

1. **There is a module for only one of the platforms**

  Sometimes a situation occurs when a cross-platform module is significantly inferior in terms of quality to a module for one of the platforms. Let’s suppose there is a module for working with images suited for the web version exclusively. Then, in the `*.web.js` file we will use this module, while for a mobile application we will find an alternative, suited for `react-native`.

1. **Significant differences in web version and application**

  Such behavior is discouraged in a cross-platform development format, but, as usual, there are exceptions. Let's say your application has an admin panel and, for some reason, you want the web application to have much more functionality. Then it makes sense to generate two files: `*.web.js` and `*.js`.

## Application entry point

Each application has its own entry point. That is, in essence, this is where your application starts from.

In startupjs structure there is the `Root` folder. This is where the `App` component, which initializes the entire application, is imported from startupjs. This is what the standard `Root/index.js` looks like:

```jsx
import { BASE_URL } from '@env'
import init from 'startupjs/init'
import orm from '../model'
import React from 'react'
import App from 'startupjs/app'
import { observer, model } from 'startupjs'
import { Platform } from 'react-native'

// Frontend micro-services
import * as main from '../main'

if (Platform.OS === 'web') window.model = model

// Init startupjs connection to server and the ORM.
// baseUrl option is required for the native to work - it's used
// to init the websocket connection and axios.
// Initialization must start before doing any subscribes to data.
init({ baseUrl: BASE_URL, orm })

export default observer(() => {
  return pug`
    App(
      apps={main}
    )
  `
})
```

Let's take a closer look at what this code does. There are some curious points.

1. `import * as main from '../main'`: imports the main application. Startupjs allows you to build multiple applications within a single project. More on this below.

1. `init({ baseUrl: BASE_URL, orm })`: initializes the current base url for the project, which is required for `react-native` (for example, to connect the application to the server) and which sets up orm. Actually, this is what is said in the comment.

1. `App(apps={main})`: the actual initialization of the entire project, including all applications. There are a few extra props here too:

    * `criticalVersion` - indicates the current version of the application, and if the current version of the application is outdated, a request to update it will appear.

    * `useGlobalInit`: сallback that will be executed after initialization. By default, initialization is to add a reference to `user` in` _session.user`. To add a reference means to create such a link, which will constantly contain the actual information about `user` in` _session.user` and vice versa, when changing `_session.user`, the changes will immediately go to `user`. But you can pass your callback to useGlobalInit, which will be executed right after the default behavior. An important point is that the passed callback must return `true` on success, since otherwise the code execution will not go to the application renderer, since it will consider that an error occurred at the time of initialization.

## ORM

At the root of the project there is a `models` folder. It is where all project models are being stored and connected. Each model is connected in the `models/index.js` file. It looks like this:

```js
import TodosModel from './TodosModel'
import TodoModel from './TodoModel'

export default function (racer) {
  racer.orm('todos', TodosModel)
  racer.orm('todos.*', TodoModel)
}
```

Let's stop here to examine each option in further detail. Pay special attention to the names of the imported models. You can see that we have a plural `TodosModel` and a single `TodoModel`. The plural model is for collection. The plurality of the name comes from the fact that the collection contains many same-type objects (for example, here the collection contains many todo objects). The singular model represents one SPECIFIC item in the collection. Let's use an example to understand it better. You have a large room filled with cats. This is a collection of cats and it is described by `CatsModel`. There are many cats - red, black and even dappled. But a specific gray cat Boris with light spots will be described by `CatModel`.  Obviously, these models will have different functionality. So you can add a new cat to the room or remove an existing one. But with a specific cat, you can do completely different things, for example, add a ribbon to it. Now let's see how we bind a specific model to a specific entity. For this we use the command `racer.orm(path, model)`.

Here, `path` is the path to a specific entity (most often it is a collection or a collection item). Usually it looks like:
  * `collectionName`: the name of the collection, in the listing above it is `todos`
  *`collectionName.*`: any specific element of the collection. In the listing above it is each `todo` in the `todos` collection.

---

Now let's figure out what the model file, which we connected above, really is. Let's look at another example. This is what a model that has only standard model methods looks like:

```js
import { BaseModel } from 'startupjs/orm'

export default class TodosModel extends BaseModel {

}
```

The `BaseModel` module, of which the `TodosModel` is a descendant, provides standard methods. The model file for a specific element will look the same.

```js
import { BaseModel } from 'startupjs/orm'

export default class TodosModel extends BaseModel {

}
```

But the main advantage of models is that they can be extended with your own methods. That is, it provides a terrific way to encapsulate some functionality in a model and access it anywhere in the application.

Let’s extend `TodosModel` by introducing the `addTodo` method, which will add the task creation date to the recorded data.

```js
import { BaseModel } from 'startupjs/orm'

export default class TodosModel extends BaseModel {

async addTodo() {
    let id = this.id()
    const createdAt = Date.now()
    await this.add({
      id,
      createdAt,
      ...data
    })

    return id
}

}
```

Another important point: `this` in this object contains the model. That is, you can work with `this` in the same way as with model. Now you can call the `addTodo` method for the `todos` collection model. For example:

```js
  const $todos = useModel('todos')

  const [title, setTitle] = useState('')

  function addTodo() {
    $todos.addTodo({
      title,
      complited: false
    })
  }

  return pug`
    Div.root
      TextInput(value=title onChange=setTitle)
      Button(onPress=addTodo)
```

You can also extend the `TodoModel` in a similar way. The key is not to mix up the collection model with a specific document.

Important! In order for the model to work in the project, it has to be initialized in `models/index.js`

## apps

After the project is initialized, you will have one application: `main`. This means that your project consists of one application. However, startupjs allows you to create several applications within one project. They will share the same server and same database, as well as the same models.

## What is a standalone application?

Open the `main` folder and look at the structure. This is the application; each application in startupjs has such a template.

But first things first: here is the `routes.js` file, and it looks like this:

```js
export default (components = {}) => [
  {
    path: '/',
    exact: true,
    component: components.PHome
  },
  {
    path: '/about',
    exact: true,
    component: components.PAbout
  }
]
```

Here we see a function that returns an array of objects. Each object is typically a rule defining which component should be displayed on a given route. The `exact` parameter indicates an exact match of the route. This means that, for example, the `PAbout` component will be displayed only after following the `/about` route.

Let us now take a look at the `index.js` file:

```js
import * as pages from './pages'
import getRoutes from './routes'

export { default as Layout } from './Layout'
export const routes = getRoutes(pages)
```

Usually, this file remains unaltered throughout the entire development phase. Its main task is to export a list of rules from `routes.js` and layout.

The `Layout` folder is self-evident. In simple terms, it is a component that will be a wrapper for every page in the current application. Therefore, here you should describe `Header`, `Footer` or `Sidebar`.

The similar situation is with `pages`. Here, the folders store the pages that will be rendered upon accessing the specified routes. After creating a new page, do not forget to export it in `pages/index.js` file. The following rule for naming components is accepted here: prefix P - indicating that this is a page, and then `uppercase` the name of the page, for example` PTodosList`.

## When does it make sense to create a new application?

A significant difference between layouts and functionality is a prerequisite for this. For example, you may have an application for which an admin panel needs to be implemented. In this case, it makes sense to initialize a new admin application. Moreover, in the future it will be relatively easier to maintain, because part of the `admin` panel and part of the application itself are in fact different entities in conceptual terms. Plus it all helps with code splitting.

## How to set up a new application?

Let us add the `admin` application to the base project.

1.	Create a folder with the name of your new application. In our case this folder will be called `admin`.
1.	Implement a basic structure, following the example of `main`. This means that you have to add `index.js`, `routes.js` and `Layout`, `pages` folders.
1.	Now you need to set it up in `Root`. Within the `index.js` file, where the application is initialized, add the `admin` application to the `App` component props. You will get the following structure:

```js
...

// Frontend micro-services
import * as main from '../main'
import * as admin from '../admin'

...

export default observer(() => {
  return pug`
    App(
      apps={main, admin}
    )
  `
})
```

## Server

Startupjs provides an Express-based server. After running `npx startupjs init myapp` you get a fully functional server, to which the orm and base application `main` are already connected. On this basis, you can already build your application’s business logic. Server can be found at `server/index.js`. Let us look closer at how a typical server looks in startupjs:

```js
import init from 'startupjs/init'
import orm from '../model'
import startupjsServer from 'startupjs/server'
import api from './api'
import initPermissions from '@dmapper/permissions/server'
import { initApp } from 'startupjs/app/server'

// Init startupjs ORM.
init({ orm })

// Check '@startupjs/server' readme for the full API
startupjsServer({
  getHead
}, (ee, options) => {
  initApp(ee)

  ee.on('routes', expressApp => {
    expressApp.use('/api', api)
  })
})

function getHead () {
  return `
    <title>HelloWorld</title>
    <!-- Put vendor JS and CSS here -->
  `
}

export default function run () {}
```

Some things to note here:

  * `init({ orm })`: initializing orm on the server side
  * `startupjsServer(option, callback)`: function that initializes the server. Generally speaking, it is the key function. It has two parameters:
    * `option`: options object. Here the routes of each application are set as well as a template for the head block in the source html file. Different startupjs extensions are also set here (for example, shardb-access, sharedb-schema, etc.)
    * `callback(ee, option)`: here `ee` is an instance of `EventEmitter`, therefore it enables subscription to various `events`. `option` is a modified copy of the first argument `startupjsServer`, generated by `startupjsServer`.
  * `initApp(ee)`: required for session initialization.
  * `ee.on('routes', callback)`: here in the callback middleware Express is connected. Thus, in the example above connection to `api` is being established, import of which can be seen above, at `/api` route.

## Force Compile Modules

By default, webpack does not go through the files in the `node_modules` folder, because the already-compiled code is usually stored there. But from time to time such situations occur, when it is essential to convert code within a module, using `babel`. In this case, add the package name to `forceCompileModules` in `webpack` configuration file. Be aware of the fact that two such files exist:

  * `webpack.server.config.cjs`: for building the server

  * `webpack.web.config.cjs`: for building the client side

A module has to be added to the corresponding configuration file, depending on which part of the project it is used in.

Let us look at the example when there is a `react-native-animatable` module that needs to be pre-compiled. Because it is used in the client side of a project, we add it to `forceCompileModules` in `webpack.web.config.cjs`:

```js
const getConfig = require('startupjs/bundler.cjs').webpackWebConfig

module.exports = getConfig(undefined, {
  forceCompileModules: [
    'react-native-animatable',
  ],
  alias: {},
  mode: 'react-native'
})
```
