Configuration

Llana's basic implementation can be configured quickly using .env values which will connect to your data source and configure other basic options.

More advanced configuration is done via data tables (_llana_*) which we add dynamically to your data source.

We do this to ensure the application is fully cloud based and there is no dependency on the file system.

Database

Replace the database connection string DATABASE_URI in the .env file.

See data sources for more information.

Hosts

You can lock down requests to specific origins by passing a comma separated list of IP addresses via the .env HOSTS property.

Warning: By default the system has no hosts and is open to all. If you are not operating a public application, we highly recommend you restrict your application to known services.

HOSTS="127.0.0.1,192.168.0.1"

Note: We are looking at the x-real-ip header value, so any proxy service will likely result in a failed host check.

Authentication

As standard we ship with two types of authentication with some examples added, however if you are developing a public application you can skip authentication by adding SKIP_AUTH=true to your .env file.

This will remove the need for any user authentication and make all your endpoints public. If you want to change public permisisons or unlock tables for public consumption, see Public Tables.

API KEY

Here are the configuration settings and their default settings:

Env KeyDefaultDetails
AUTH_USER_API_KEY_LOCATIONHEADERThe location of where the API Key is passed in the requests, options are: HEADER QUERY BODY
AUTH_USER_API_KEY_NAMEx-api-keyThe key of the key:value pair passed, e.g. x-api-key:YOURKEY
AUTH_USER_TABLE_NAMEUserThe main identity table containing your user identity
AUTH_USER_IDENTITY_COLUMNundefinedThe column containing your users identity key in your users identity table, defaults to the primary key of the identity table
AUTH_USER_API_KEY_FIELDUserApiKey.apiKeyThe column of the users API key accessed from the main identity table

Note: if you specify a SOFT_DELETE_COLUMN it will ensure this value is NULL before authorizing.

JWT Token

We provide some special endpoints that allows you to exchange a username/password for a access_token. You can then pass the access token in future requests.

Login

Example Request:

POST `/auth/login`

body: {
  username: test@test.com,
  password: test
}

Example Response:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsInVzZXJuYW1lIjoidGVzdEB0ZXN0LmNvbSIsImlhdCI6MTcyNTM1NDI4OCwiZXhwIjoxNzI1NDQwNjg4fQ.Wb_Y6y1Mkb0xrhUX57s2XlKtqeZ5L68wNCVMWT8wFJw",
    "id": 1
}

Profile

You can test your access token by using the /auth/profile endpoint which also returns your users table data.

GET `/auth/profile`

Example Response:

{
    "id": 1,
    "email": "test@test.com",
    "first_name": "Jon",
    "last_name": "Doe"
}

JWT Configuration

You will need to add / update your .env values to fit your database schema:

Env KeyDefaultDetails
AUTH_USER_TABLE_NAMEUserThe main identity table containing your user identity
AUTH_USER_IDENTITY_COLUMNundefinedThe column containing your users identity key, defaults to the primary key of the identity table
AUTH_USER_TABLE_USERNAME_FIELDemailThe column of your username field in the user identity table
AUTH_USER_TABLE_PASSWORD_FIELDpasswordThe column of your password field in the user identity table
AUTH_USER_TABLE_PASSWORD_ENCRYPTIONBCRYPTThe encoding type for your password field, options are: BCRYPT SHA1, SHA256, SHA512, MD5, ARGON2
AUTH_USER_TABLE_PASSWORD_SALT10Optional salt used in conjunction with the encryption
BASE_URL_APIBase URL for your Llana backend API, used for making API requests and securing JWT cookies. For example http://localhost:3000 if you're working locally, or http://your-llana-backend-url.com
BASE_URL_APPBase URL for your Llana frontend application, used for linking frontend resources. Must be configured correctly to allow cross-origin (CORS) communication with JWT cookies between your frontend and backend. For example http://localhost:3001 if you're working locally, or http://your-llana-frontend-url.com
AUTH_COOKIES_DOMAINThe domain to which authentication cookies should be restricted. This should be the top-level domain of your application’s front-end server. For example, if you're using Llana as your front-end, set this to your-frontend-domain.com. If you’re using a different front-end server, such as Next.js or Nuxt, use the domain corresponding to that server instead. If left unset, will default to the hostname of BASE_URL_API
JWT_KEYSecret key used for signing JSON Web Tokens (JWTs) for user authentication. Keep this key confidential and secure.
JWT_REFRESH_KEYSecret key used for signing JWT refresh tokens, enabling secure token renewal. Keep this key confidential and secure.
JWT_EXPIRES_IN15mExpiration duration for regular JWT tokens, indicating how long a token remains valid.
JWT_REFRESH_EXPIRES_IN14dExpiration duration for JWT refresh tokens, specifying how long the refresh token is valid for token renewal.

You can use NodeJS to generate secure JWT keys by running this command in a terminal:

$ node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

Password Encryption

To help ensure user passwords are always stored in an encrypted format, we perform checks on CREATE and UPDATE requests. If the request includes an update to your users identity table and includes the password field, we will encrypt the password using your configures (or default) encryption method.

This means you can delegate the user password encryption entirely to Llana.

Future Development

TODO: Add the ability to maintain routes via a web portal

Public Tables

By default authentications apply to all tables, however you can specify tables for public access and their access permissions.

You can maintain routes in the table _llana_public_tables.

FieldTypeDetails
tablestringThe table this rule applies to
access_levelenumThe permission level to the public, either READ WRITE DELETE
allowed_fieldsstringA comma separated list of fields that are available

This works well in combination with roles (see below) for giving access to relevant data across different access levels.

Roles

User roles are an important part of granting the correct permissions to perform relevant actions on the API endpoints.

Firstly, you should configure where your roles are located:

Env KeyDefaultDetails
ROLE_LOCATION_USER_TABLE_NAME$AUTH_USER_TABLE_NAMEThe table containing your roles, defaults to your auth users table
ROLE_LOCATION_USER_TABLE_ROLE_FIELDroleThe column containing your users role type
ROLE_LOCATION_USER_TABLE_IDENTITY_COLUMNundefinedThe column containing your users identity key, defaults to the primary key of the identity table

You can manage roles via the _llana_role table.

FieldTypeDetails
custombooleanIf this is a custom role (applies to specific endpoints)
tablestringIf not default, which table does this restriction apply to
identity_columnstringIf not default and the primary key of the table is not the user identifier, which column should be used to identify the user
rolestringThe name of the role, which should match the value from your users role field
recordsenumThe permission level for this role across all records in the table, either NONE READ WRITE DELETE
own_recordsenumThe permission level for this role if it includes a reference back to the user identity (their own records) either NONE READ WRITE DELETE
allowed_fieldsstringA comma separated list of fields that are available for this role

By default we will add the following roles to the table if non-exist on application startup:

CustomTableIdentityRoleRecordsOwn Records
falseADMINDELETE
falseUSERREAD
true$AUTH_USER_TABLE_NAMEADMINDELETEDELETE
true$AUTH_USER_TABLE_NAMEUSERNONEWRITE
true$AUTH_USER_API_KEY_TABLE_NAME$AUTH_USER_API_KEY_TABLE_IDENTITY_COLUMNADMINDELETEDELETE
true$AUTH_USER_API_KEY_TABLE_NAME$AUTH_USER_API_KEY_TABLE_IDENTITY_COLUMNUSERNONEWRITE

Role permissions work progressively, which means:

  • DELETE - Has full permission to DELETE WRITE and READ
  • WRITE - Can also READ records
  • READ - Can only READ records and has no WRITE permissions
  • NONE - Has no access to the table

If you want to cherry pick the data coming back to the user based on role, you can add a comma separated list of fields the user can see in the allowed_fields field, all other fields will be removed from the returned data.

If the user has insufficient permissions they will get a 403 Forbidden response.

#### Future Development

TODO: Add the ability to maintain routes via a web portal

Relations

In some situations you may have data sources which don't support relations but you still want a way of "relating" data together.

Imagine having two Google Sheets and you want to link them in some way. You can create "manual references" which will allow Llana to perform the relationship binding for you.

You can manage relations via the _llana_relation table.

FieldTypeDetails
tablestringThe name of the data source for the relation
columnstringThe field name containing the lookup value
org_tablestringThe name of the original data source
org_columnstringThe name of the field, which should match the value from your data source relation field

Example:

{
  "table": "Customer",
  "column": "id",
  "org_table": "SalesOrder",
  "org_column": "CustId"
}

This will create the relation on the SalesOrder table and link any customers by CustId > Customerid

#### Future Development

TODO: Add the ability to maintain routes via a web portal

System Cache

Out of the box we cache table schema and some user auth/identity data to help speed up requests and reduce load on the database.

The cache is reset each time the application boots up, this means if you make database schema changes, you can simply reboot the application and the cache will be cleared.

This allows us to set a longer-lived cache ttl, however, if you want to change these, you can set the, in the .env file. Setting a ttl to 0 will result in no cache being used.

CACHE_TABLE_SCHEMA_TTL=3600000 #value in milliseconds
CACHE_IDENTITY_DATA_TTL=60000 #value in milliseconds

Table / Request Caching

Advanced caching is very useful for slow or common queries which you would like Llana to cache on the backend to deliver faster results via your API.

IF enabled, the lifecycle works as follows:

  • GET requests matching the exact parameters are returned from the cache
  • CREATE/UPDATE/DELETE requests on the table, flag that the cache should be updated before the next expiry event
  • A backend scheduler checks if the cache needs regenerating and if the cache has expired, it will then regenerate the data and replace the cached data

Configuration

To turn on the table / request cache, add the following .env value: USE_DATA_CACHING. If set, Llana will add the table _llana_data_caching on boot-up.

This table will allow you to specify which requests should be cached and monitor for caching changes.

FieldTypeDetails
tablestringThe table we are caching data
requeststringThe query data to look for on the request, if matched, return the cached result
ttl_secondsnumberThe time in seconds for how long the cache should last
expires_atdateThe date the current cached data expires at
refreshed_atdateThe date when the data was previously refreshed at
data_changed_atdateThe date when data in the table changed, avoid regenerating cached data if nothing changed and reducing load on your Llana instance.

By default the scheduled task will run every 60 seconds and check if any data needs reloading, if you wish to change the frequency set the CronExpression value in the CRON_EXPRESSION_CACHE_CHECK value in your .env file.

Example

Imagine you have a search box in your frontend application and you want to be able to search orders by customer name. You want to show results in real time as the user enters data.

Adding this request into the _llana_data_caching table will result in these requests being returned form the cache rather than directly from the database. The backend scheduler will monitor for table changes and regenerate the cache every 5 minutes (60*5 = 300) if the data has changed in that time.

tablerequestttl_seconds
Orders?fields=orderId,Customer.ContactName&relations=Customer&limit=99999&offset=0300

This will reduce the load on your backend instance and make your frontend user experience much faster.

Redis

As standard Llana will use an in memory cache, which is not ideal for large applications. You can leverage Redis by adding a redis service.

Include the following environment variables in your .env file to configure Redis:

REDIS_HOST=127.0.0.1  # Replace with your Redis server's IP address
REDIS_PORT=6379       # Replace with your Redis server's port
REDIS_USER=default    # Replace with your Redis username (optional)
REDIS_PASS=           # Replace with your Redis password (optional)
  • REDIS_HOST: The hostname or IP address of the Redis server.
  • REDIS_PORT: The port number Redis is running on.
  • REDIS_USER: The username your authentication accepts (if specified)
  • REDIS_PASS: The password your authentication accepts (if specified)

If REDIS_HOST && REDIS_PORT exist, it will use the Redis cache over the in memory cache.

Documentation

As standard we generate OpenApi JSON and a Redoc page which can be viewed from the homepage of your instance. You can disable docs by adding the .env value SKIP_DOCS=true

When the application boots up, it will build the OpenApi JSON schema based on your data source schema. If you make changes to your data source, simply reboot the application and the changes will be reflected in your documentation.

The OpenApi JSON is accessible via /openapi.json, you can use this to power postman or any 3rd party API tooling.

We use this to display a Redoc page at the root / of your instance. Here are the .env values you can customize.

Env ValueDefault
DOCS_TITLEAPI Documentation

Deleting

As standard the delete endpoint will physically remove the record from your database. You can provide the .env value for SOFT_DELETE_COLUMN e.g. deletedAt or deleted_at to perform a soft delete across your database.

If you are using soft delete, you can request a hard delete by passing the query param ?hard=true with your delete request. Note: anything truthy with the hard= will skip the soft delete and perform a hard delete.

Logging

You can update logging levels via a .env value, for more details see: Developers > Debugging/Logging

Timezones

You can set a timezone globally using nodes default TZ value in your .env

Llana's timezone should match your data source, for example if your data source is running on UTC then you can add TZ=UTC to your .env

If they are not hosted in the same location you may need to set this manually.

Examples

Here are some common example configuration use cases:

Restricted By Host, No User Authentication

This use case could be an internal database, not accessed by clients but connected to other internal services.

  • It is locked down by HOSTS which means only that host can access, everything else will get a 403 Forbidden response.
  • SKIP_AUTH has been added, so no user authentication is requires, all requests coming from the valid host will pass.
DOMAIN="llana.domain"
DATABASE_URI=********
HOSTS="123.123.123.123"
SKIP_AUTH=true
SKIP_DOCS=true