type-arango provides TypeScript decorators to describe collections, document and routes for ArangoDB Foxx Microservices.
A document represents a single entry of a collection.
- Entity - provides ORM functions to document instances
- entity.insert - creates a document
- entity.merge - merges an object into a document
- entity.replace - replaces a document
- entity.remove - deletes a document
- entity.save - saves property changes
- entity._saveKeys - returns a list of modified properties
- entity.related - returns related document/s
- @Document - initializes a new document
- @Edge - initializes a new document of an edge collection
- @Nested - initializes a nested document
- @FromClient - parse request body before writing to database
- @ForClient - parse document before sending to client
- @Attribute - defines property name and type as document attribute
- @Authorized - protects the property with read / write roles
- @Index - creates an index for a property
- @OneToOne - defines a 1:1 relation
- @OneToMany - defines a 1:n relation
A collection contains documents and provides routes and other utilities.
- Entities - provides ORM functions
- entities.find - returns document instances of the collection
- entities.findOne - returns single document instance
- @Collection - initializes a collection
- @Route.use - initializes routes by method
- @Route.groups - defines roles for CRUD routes
- @Route.roles - creates roles for requests by utilizing the client session
- @Route.auth - authorizes a request depending on a document
- @Route.LIST - initializes a special route for fetching a list
- @AQLFunction - register an AQL function
- @Task - periodically execute a function
Types are used to better describe common patterns to store and retrieve attribute data.
- 🌐 Type.I18n - Internationalization support
- 💱 Type.Currencies - Support for multiple currencies
- 🕒 Type.DateInsert - Set attribute to
new Date
when creating documents - 🕘 Type.DateUpdate - Set attribute to
new Date
when updating documents
- Configuration - Options for
typeArango()
- @Description - Decorator for describing Classes or Properties
- 📜 Enjoi
(Enjoi, Joi) => Joi
- Enhanced Joi making use of Types - Client operators - Clients can provide operators inside query parameter values
- "CRUD-like" - explained
The configuration can be passed into the typeArango function.
const complete = typeArango({
/**
* Available log levels are `Error`, `Warn`, `Info` & `Debug`
*/
logLevel: LogLevel = LogLevel.Warn;
/**
* Prefix the collection name by applying `module.context.collectionName` to it
*/
prefixCollectionName: boolean = false;
/**
* Display the source of your routes in Swagger
*/
exposeRouteFunctionsToSwagger: boolean = true;
/**
* Dasherize endpoints (eg `UserProfiles` becomes `user-profiles`)
*/
dasherizeRoutes: boolean = true;
/**
* Separator used to split a parameter value (ie /?x=LIKE|y)
*/
paramOperatorSeparator: string = '|'
/**
* Always add field writer roles to field reader roles
* By default an `@Authorized(readers => ['user'], writers => ['admin'])`
* evaluates to `readers = ['users','admin'], writers = ['admin']`
*/
addAttributeWritersToFieldReaders: boolean = true;
/**
* When using Type.I18n the defaultLocale is used when other locales do not match
*/
defaultLocale: string = 'en';
/**
* Whether to strip the `_id` key from documents
*/
stripDocumentId: boolean = true;
/**
* Whether to strip the `_rev` key from documents
*/
stripDocumentRev: boolean = true;
/**
* Whether to strip the `_key` key from documents
*/
stripDocumentKey: boolean = false;
/**
* Whether to execute aqlfunctions.unregisterGroup for every collection
* Set to false when using custom AQL functions outside of type-arango
*/
unregisterAQLFunctionEntityGroup: boolean = true;
/**
* List of roles that are available for every request
*/
providedRolesDefault: string[] = ['guest']
/**
* List of required roles for a route when no other roles are defined
*/
requiredRolesFallback: string[] = ['user']
/**
* List of required writer roles for a route when no other roles are defined
*/
requiredWriterRolesFallback: string[] = ['admin']
/**
* Returns the roles of the current viewer user
*/
getUserRoles = function(req: Foxx.Request): string[] {
return (req.session && req.session.data && req.session.data.roles || []).concat('guest');
};
/**
* Returns all authorized roles for a request
*/
getAuthorizedRoles = function(providedRoles: string[], requiredRoles: string[]): string[] {
return providedRoles.filter((role: string) => requiredRoles.includes(role));
}
/**
* HTTP Status to return when an unauthorized (no auth provided) request occurs
*/
throwUnauthorized: ArangoDB.HttpStatus = 'unauthorized';
/**
* HTTP Status to return when an forbidden (invalid auth provided) request occurs
*/
throwForbidden: ArangoDB.HttpStatus = 'unauthorized';
/**
* Applied on client data when using `json()` inside a route
*/
fromClient?: (doc: DocumentData, opt: RequestInfo) => DocumentData;
/**
* Applied on response data when using `send()` inside a route
*/
forClient?: (doc: DocumentData, opt: RequestInfo) => DocumentData;
});
// initialize documents and collection after calling typeArango
import * as _Collections from './collections';
// completing the setup
complete();
The Entity class is primarily used to provide ORM functions to document instances. Under the hood it uses a Proxy to keep track of property changes and provide schema validation.
Extend all documents from the Entity
class provided by type-arango:
import { Document, Entity } from 'type-arango'
@Document()
class User extends Entity { ... }
When using the entity inside a route, it comes with handy ORM features:
static route(){
// create a user instance
const user = new User({email:'contact@example.com'});
// save the user to the collection
user.insert();
// change the user and return a list of modified properties
user.name = 'RienNeVaPlus';
console.log(user._saveKeys); // => ['name']
// save the changes
user.save();
}
Stores the instance to the collection. Throws when the document already exists. It's really just an alias for entity.save({update:false})
.
Example (in route)
// create entity
const user = new User({email:'hello@example.com'});
// store in collection
user.insert();
Merges doc
into the entity, it's as simple as Object.assign(this, doc);
.
Example (in route)
// load an user instance
const user = Users.findOne('123');
// merge request body into entity
user.merge(json());
Replaces the document with the provided object, ignoring _saveKeys
.
- doc
{[key: string]: any}
- Object to replace the current document. - options?
ArangoDB.ReplaceOptions
- See ArangoDB manual.
Example (in route)
// load an user instance
const user = Users.findOne('123');
user.name = 'This will be ignored';
// replace the user using Foxx collection._replace
user.replace({email:'test@example.com'}, {overwrite:true});
Removes the document from the collection using collection._remove
- options?
ArangoDB.RemoveOptions
- See ArangoDB manual.- waitForSync?
boolean
- overwrite?
boolean
- returnOld?
boolean
- silent?
boolean
- waitForSync?
Example (in route)
// load an user instance
const user = Users.findOne('123');
// deletes the document from the collection
user.remove();
Saves the values of all changed attributes (entity._saveKeys
) to the documents collection. Creates a new document when no _key
is provided. Use the option {update:false}
to always create a new document even when a _key
is available.
- options?
EntitySaveOptions
- See ArangoDB manual for insert / save.- keepNull?
boolean
- mergeObjects?
boolean
- waitForSync?
boolean
- silent?
boolean
- returnNew?
boolean
- keepNull?
Example (in route)
// load an user instance
const user = Users.findOne('123');
// deletes the document from the collection
user.remove();
Returns a list of unsaved / modified properties. Is used by entity.save
in order to determine which attributes need to be written.
Example (in route)
// load an user instance
const user = Users.findOne('123');
// modify the user
user.name = 'RienNeVaPlus';
// return a list of modified properties
console.log(user._saveKeys); // => ['name']
// save changes
user.save();
// _saveKeys is now empty
console.log(user._saveKeys); // => []
Returns the related document/s of attributes decorated with @OneToOne
or @OneToMany
.
- attribute
string
- Attribute to load the related document from. - keepAttributes?
string[]
- List of attributes to load from the collection, default is all attributes.
Some relations have values, but these are mainly used for fetching the related document. Type-arango overwrites these values with the fetcher function described here. However the original value is available by simply prefixing the property key with an underscore (eg entity._profile
).
Example (in route)
// in a route
const user = Users.findOne('1');
// returns an address entity instance
const address = user.related('address');
// returns a profile entity instance limited to the selected attributes
const profile = user.related('profile', ['attributes','to','select']);
// read the profile id when stored inside user.profile
const profileId = user.profile;
Decorates a class that has been extended by Entity
. Documents are consumed by @Collection(of => Document)
and define a schema which is derived from the property types and additional decorator information.
Example
@Document()
class User extends Entity { ... }
Decorates a class that has been extended by Entity
. Edges are consumed by @Collection(of => Edge)
and define a schema which is derived from the property types and additional decorator information.
Example
@Edge()
class Relation extends Entity { ... }
Documents in ArangoDB can be nested. Make sure to define nested classes before the documents.
Example
@Nested()
class UserPerson {
@Attribute()
gender: string;
}
@Document()
class User extends Entity {
@Attribute()
person: UserPerson;
}
Applied on client data when using json()
inside a route.
Example
@Document()
@FromClient(doc => Object.assign(doc, {requestTime:new Date()})
class User extends Entity {}
Applied on response document when using send()
inside a route.
Example
@Document()
@ForClient(doc => Object.assign(doc, {requestTime:new Date()})
class User extends Entity {}
Defines an attribute of the document. Uses metadata reflection to derive a Joi schema for the attribute by using the TypeScript type. Schemas will be validated when using routes or modifying entity instances.
- schema?
(enjoi: Enjoi, joi: Joi) => Joi
- Function where the first argument is aJoi
type of the property metadata type (Joi.string()
in the example below). - readers?
string[]
- Roles with read permission to the attribute. - writers?
string[]
- Roles with write permission to the attribute.
For more details on roles, see @Authorized()
.
Example
@Document()
class User extends Entity {
// attribute has to be an email address
@Attribute(string => string.email())
email: string;
// attribute has to be a positive integer with a max of 100
@Attribute(number => number.integer().positive().max(100))
age: number;
}
Defines reader
and writer
roles to protect attributes in routes. The the 2nd example for details on role authorization.
- readers?
string[]
- Roles with read permission to the attribute. - writers?
string[]
- Roles with write permission to the attribute.
Example
...
@Attribute()
@Authorized(readers => ['user'], writers => ['admin'])
name: string;
// roles can also be defined by only using attribute
@Attribute(readers => ['viewer'], writers => ['viewer','admin'])
age: number;
// even when the attribute has a type
@Attribute(string => string.email(), readers => ['viewer'], writers => ['viewer'])
email: string;
...
Creates an index on the attribute.
- additionalFieldsOrType?
string[] | ArangoDB.IndexType
- List of additional fields for the index or the index type. - options? ``
- type?
"hash" | "skiplist" | "fulltext" | "geo"
- additionalFields?
string[]
- sparse?
boolean
- unique?
boolean
- deduplicate?
boolean
- type?
Warning: Creating an index on an existing collections can take a some time.
Example
...
@Index(type => 'hash')
@Attribute()
name: string;
@Index(['height'], {type:'skiplist',sparse:true})
@Attribute()
age: number;
...
Defines a 1:1 relation to another entity. Decorated properties have additional functions to fetch related entities, see entity.relation
. See also entity.related('attribute')
.
- type?
Entity
- The related document entity. - relation?
(TypeEntity) => TypeEntity.attribute
- TypeEntity is an object with the same keys as the related entity and can be used to create a relation to a certain field. The default relation isEntity._key
.
Example
@Document()
class User extends Entity {
// use @Attribute when relational data is stored in document
@Attribute(string)
@OneToOne(type => Address)
primaryAddress: Related<Address>;
// don't use @Attribute when the property is "virtual" and relational data is stored on the other end
@OneToOne(type => Profile, Profile => Profile.owner)
profile: Related<Profile>;
}
Defines a 1:n relation to another entity. Mostly the same as @OneToOne
except requesting the relation returns an array of entity instances instead of a single instance. See also entity.related('attribute')
.
- type?
Entity
- The related document entity. - relation?
(TypeEntity) => TypeEntity.attribute
- See@OneToOne
.
Example
@Document()
class User {
@OneToMany(type => Address, Address => Address.owner)
addresses: Related<Address[]>;
}
The @Before.*
and @After.*
decorators can be used as ClassDecorator
(to apply a listener to a document) or as PropertyDecorator
(to apply a listener to an attribute).
Both decorators provide the same methods with a slightly different resolver syntax.
- Single listeners
.document(resolver)
- Document is loaded..insert(resolver)
- Document is inserted..update(resolver)
- Document is updated..patch(resolver)
- Document is patched..remove(resolver)
- Document is removed.
- Combined listeners
.modify(resolver)
- Document is either updated or patched..write(resolver)
- Document is either inserted, updated or patched.
Warning: Resolvers are executed when using CRUD-Routes, the methods
document
,insert
,update
,replace
andremove
of RouteArg orsave
,insert
,replace
&remove
of an Entity - but not when usingquery
.
Executes the resolver before data from the database is read / inserted / updated / replaced or removed.
Example
@Document()
// avoids deletions
@Before.remove(doc => false)
class User {
@Attribute()
// use `new Date` as a default value when inserting documents
@Before.insert(value => value || new Date)
createdAt: Date;
}
Executes the resolver after data from the database has been read / inserted / updated / replaced or removed.
Example
const MAP = ['one','two','three']
@Document()
// add virtual field to documents
@After.document(doc => Object.assign(doc, {extra:'free'}))
class User {
@Attribute()
@After.document(value => MAP[value])
numericIndex: Date;
}
DocumentData
- any object that is - or will - become a documentArangoDB.HttpStatus
- String of http status code (egforbidden
ornot-found
).Passive
-true
orundefined
- Does nothingCancel
-false
orArangoDB.HttpStatus
- Cancels the operation
// Before a document will be loaded. The callback can cancel the request.
@Before.document( (loadDocumentKey: string, {_key, method}) => Passive | Cancel )
// After a document has been loaded. Can modify the loaded document before it's returned.
// Changes to loadedDocument are temporary until the response has been sent (like forClient).
@After.document( (loadedDocument: DocumentData, {_key, document, method}) => Passive | Cancel | DocumentData )
// Before a document is inserted. Can modify the document before it's written.
// Changes to insertDocument are permanent (like fromClient).
@Before.insert( (insertDocument: DocumentData, {json, method}) => Passive | Cancel | DocumentData)
// After a document has been inserted. Can modify the inserted document before it's returned.
// Changes to insertedDocument are temporary until the response has been sent (like forClient).
@After.insert( (insertedDocument: DocumentData, {_key, document, method}) => Passive | Cancel | DocumentData )
// Before a document is updated. Can modify the document before it's written.
// Changes to updateDocument are permanent (like fromClient).
@Before.update( (updateDocument: DocumentData, {_key, json, method}) => Passive | Cancel | DocumentData )
// After a document has been updated. Can modify the updated document before it's returned.
// Changes to updatedDocument are temporary until the response has been sent (like forClient).
@After.update( (updatedDocument: DocumentData, {_key, document, method} ) => Passive | Cancel | DocumentData )
// Before a document is replaced. Can modify the document before it's written.
// Changes to replaceDocument are permanent (like fromClient).
@Before.replace( (replaceDocument: DocumentData, {_key, json, method}) => Passive | Cancel | DocumentData)
// After a document has been replaced. Can modify the document before it's returned.
// Changes to replacedDocument are temporary until the response has been sent (like forClient).
@After.replace( (replacedDocument: DocumentData, {_key, document, method}) => Passive | Cancel | DocumentData )
// Before a document is removed. Can avoid deletions.
@Before.remove( (removeDocumentKey: string, {_key, method}) => Passive | Cancel )
// After a document has been removed.
@After.remove( (removedDocumentKey: string, {_key, method}) => Passive )
// Before a document will be loaded. Rarely needed, available for the sake of completeness
@Before.document( (loadDocumentKey: string, {_key,attribute,method} ) => void )
// After an attribute has been loaded. Can modify the attribute before it's returned.
// Changes to attributeValue are temporary until the response has been sent (like forClient).
@After.document( (attributeValue: any, {_key, document, attribute, method}) => any )
// Before a document in inserted. Can modify the attribute before it's written.
// Changes to attributeValue are permanent (like fromClient).
@Before.insert( (insertAttributeValue: any, {json, attribute, method}) => any )
// After a document has been inserted. Can modify the inserted document before it's returned.
// Changes to insertedAttributeValue are temporary until the response has been sent (like forClient).
@After.insert( (insertedAttributeValue: any, {_key, document, attribute, method}) => any )
// Before a document is updated. Can modify the attribute before it's written.
// Changes to attributeValueToUpdate are permanent (like fromClient).
@Before.update( (updateAttributeValue: any, {_key, json, attribute, method}) => any )
// After a document has been updated. Can modify the updated attribute before it's returned.
// Changes to updatedAttributeValue are temporary until the response has been sent (like forClient).
@After.update( (updatedAttributeValue : any, {_key, document, attribute, method}) => any )
// Before a document is replaced. Can modify the attribute before it's written.
// Changes to documentToReplace are permanent (like fromClient).
@Before.replace( (replaceAttributeValue: any, {_key, json, attribute, method}) => any )
// After a document has been replaced. Can modify the attribute before it's returned.
// Changes to replacedDocument are temporary until the response has been sent (like forClient).
@After.replace( (replacedAttributeValue : any, {_key, document, attribute, method}) => any )
// Before a document is removed.
@Before.remove( (removeDocumentKey: string, {_key, attribute, method}) => void )
// After a document has been removed.
@After.remove( (removedDocumentKey: string, {_key, attribute, old, method}) => void )
Note: Don't use listeners when you can use
@ForClient
/@FromClient
instead.
The Entities class is primarily used to provide the functions find
and findOne
to collection instances.
Extend all collection from the Entities
class provided by type-arango:
import { Collection, Entities } from 'type-arango'
@Collection()
class User extends Entities { ... }
When using the collection inside a route, it comes with handy ORM features:
static route(){
// returns a single User instance
const user: User = Users.findOne('myDocumentKey');
// returns a list of matching User instances
const users: User[] = Users.find({filter:{name:'RienNeVaPlus'},limit:10});
}
Returns a list of entity instances.
- options
FilterOptions
- filter?
QueryFilter
- Object of values to filter the collection.- value?
value | [operator, value]
- Filter value can be an array with an operator like!=
or>
etc.
- value?
- sort?
string[]
- Sorts the results AQL style, i.e.['email DESC','name ASC']
. - limit?
number | [offset, count]
- Limits the results AQL style i.e.[10, 2]
. - keep?
string[]
- List of attributes to load from collection - unset?
string[]
- Instead of selecting attributes withkeep
,unset
returns every other attribute, except the provided ones.
- filter?
Example
static route(){
// returns a single User instance
const user: User[] = Users.find({filter:{email:['LIKE', '%@gmail.com']});
}
The same as entities.find
except it returns a single instance instead of an array and options
can be a string alias for {filter:{_key:options}
.
- options
FilterOptions | _key
- Seeentities.find
.
Example
static route(){
// returns a single User instance
const user: User = Users.findOne('123');
}
Decorates a class that has been extended by Entities
. Collections consume @Document
s and provide routes.
- ofDocument
Entity | () => Entity
- the entity of the documents in the collection (can be omitted when options are provided). - option?
ArangoDB.CreateCollectionOptions
- See ArangoDB Manual.- of?
string
- Alias for argumentofDocument
. - name?
string
- Collection name, by default the class name is used. - creators
string[]
- List of default creator roles. - readers
string[]
- List of default reader roles. - updaters
string[]
- List of default updater roles. - deleters
string[]
- List of default deleter roles. - auth
string[]
- Alias for @Route.auth. - roles
string[]
- Alias for @Route.roles. - routes
Array<Route | string>
- List of default routes to use - see @Route.*. - relations
string[] | true
- List of related attributes that can be read from client request to any route of the collection. Can also be set totrue
to expose all related attributes. - waitForSync?
boolean
- journalSize?
number
- isVolatile?
boolean
- isSystem?
boolean
- keyOptions?
KeyOptions
- type?
"traditional" | "autoincrement"
- allowUserKeys?
boolean
- increment?
number
- offset?
number
- type?
- numberOfShards?
number
- shardKeys?
string[]
- replicationFactor?
number
- of?
Example
@Collection(of => User)
class Users extends Entities { ... }
Shortcut for creating multiple routes. Creates GET
, POST
, PUT
, PATCH
, DELETE
& LIST
routes for the collection.
- ...methods
string[]
- List of methods or a preset name. - options?
Partial<RouteOpt>
- See RouteOpt.
Example
@Collection(of => Company)
@Route.use('GET', 'POST', 'PATCH', 'PUT', 'DELETE')
class Companies extends Entities { ... }
Instead of writing out all the methods, extremely lazy developers can use common preset strings.
Preset | GET | POST | PATCH | PUT | DELETE | LIST |
---|---|---|---|---|---|---|
ALL+ or * |
⚫ | ⚫ | ⚫ | ⚫ | ⚫ | ⚫ |
ALL |
⚫ | ⚫ | ⚫ | ⚫ | ⚫ | ⚪ |
CRUD+ |
⚫ | ⚫ | ⚫ | ⚪ | ⚫ | ⚫ |
CRUD |
⚫ | ⚫ | ⚫ | ⚪ | ⚫ | ⚪ |
Sets roles for all CRUD-like routes.
- creators
() => string[] | string[]
- Required roles forPOST
requests. - readers
() => string[] | string[]
- Required roles forGET
andLIST
requests. - updaters
() => string[] | string[]
- Required roles forPUT
andPATCH
requests. - deleters
() => string[] | string[]
- Required roles forDELETE
requests.
Example
@Collection(of => User)
// setup global roles for all requests below
@Route.groups(
creators => ['guest'],
readers => ['user']
)
@Route.use(['POST','GET'])
class Users extends Entities { ... }
The ClassAndProperyDecorators
can be applied on either a static method or a class. When a class is decorated the route will behave as expected from the route method.
For additional details on these routes checkout the Swagger Docs at the API
tab inside of the ArangoDB Web Interface.
Takes a function to append additional roles for all requests to any route of the collection. It's mainly for generating user specific roles from the client session
, eg adding a viewer
role fow own documents.
- roleFunction
(arg: RouteRolesArg) => string[]
- Function returning additional roles to grant. TheRouteRolesArg
contain useful tools and information:- req
Foxx.Request
- res
Foxx.Response
- session
(set?: Partial<Foxx.Session>) => Foxx.Session
- Function to read or write the current session - _key?
string
- Document key of the current request when available - document
() => DocumentData
- Loads & caches the document for the lifetime of the request. - collection
ArangoDB.Collection
- The ArangoDB collection object. - path
string
- The current path. - method
"get" | "post" | "put" | "patch" | "delete"
- _
(strings: TemplateStringsArray, ...args: any[]) => any[]
- Fetch documents with AQL - this is a shortcut forquery(aql`...`).toArray()
. - aql
ArangoDB.aql
- The ArangoDB AQL function used for queries. - query
(query: ArangoDB.Query, options?: ArangoDB.QueryOptions) => ArangoDB.Cursor
- Execute a query the conventional way, example:query(aql`...`)
. Use the less verbose_`...`
whenever possible. - db
ArangoDB.Database
- roles?
string[]
- requestedAttributes
string[]
- hasAuth
boolean
- auth?
RouteAuthorize
- error
(status: ArangoDB.HttpStatus, reason?: string) => Foxx.Response
- Send an error response
- req
Example
@Collection(of => User)
// adds 'viewer' to userRoles when requesting a document where `_key` equals `uid` of the session
@Route.roles(({session, _key}) => session().uid === _key ? ['viewer'] : [])
class Users extends Entities { ... }
Takes a function to determine access permission on a document level. Used whenever there is no other way of determine the permission than deriving them from the document itself. Might cause an additional read, so it is preferred to use Route.roles
whenever possible.
- authorizeFunction
(arg: RouteAuthArg) => boolean
- Function returning whether the document can be accessed. TheRouteAuthArg
contain useful tools and information:- req
Foxx.Request
- res
Foxx.Response
- session
(set?: Partial<Foxx.Session>) => Foxx.Session
- Function to read or write the current session - method
"get" | "post" | "put" | "patch" | "delete"
- action
"create" | "read" | "update" | "delete"
- document
DocumentData
- Requested document - doc
DocumentData
- Alias fordocument
- req
- method -
Example
@Collection(of => User)
// allows access to documents with an user attribute qual to session.uid
@Route.auth(({doc, session}) => doc.user === session.uid)
class Users extends Entities { ... }
All route functions receive a single argument, the RouteArg
which contains useful information and tools to describe, authenticate, read and answer requests. A lot of them are well known from the Foxx routes.
- req
Foxx.Request
- res
Foxx.Response
- method
"get" | "post" | "put" | "patch" | "delete"
- action
"create" | "read" | "update" | "delete" | "list"
- path
string
- Path of the route. - param
{[key: string]: any}
- Object of (only) valid path- and query parameters. - validParams
string[]
- List of path- and query parameter names. - roles
string[]
- Roles used to authorize the request. - userRoles
string[]
- All roles of the client. - collection
ArangoDB.Collection
- Collection object of the entity. - _key
string
- Shortcut forparam._key
- exists
(name: string) => boolean
- Shortcut forcollection.exists
- document
(key = _key) => Document
- Resolves and caches a document for the lifetime of the request. Avoids duplicate reads. Loads the current document by default, can load other documents when called with an argument. - relations
(data: DocumentData) => DocumentData
- Loads and adds related entities (provided byreq.queryParams.relations
) to result. 📘 About relations - insert
(data: DocumentData) => ArangoDB.InsertResult
- Inserts a document into the collection, executes callbacks from@On.insert
. - update
(dataOrKey = _key, dataOrOptions?, options?) => ArangoDB.UpdateResult
- Updates a document of the collection, executes callbacks from@On.update
. Uses_key
as default document key. - replace
(dataOrKey = _key, dataOrOptions?, options?) => ArangoDB.UpdateResult
- Replaces a document of the collection, executes callbacks from@On.replace
. Uses_key
as default document key. - remove
(keyOrOptions = _key, options?) => ArangoDB.RemoveResult
- Removes a document of the collection, executes callbacks from@On.remove
. Uses_key
as default document key. - _
(strings: TemplateStringsArray, ...args: any[]) => any[]
- Fetch documents with AQL - this is a shortcut forquery(aql`...`).toArray()
. - query
(query: ArangoDB.Query, options?: ArangoDB.QueryOptions) => ArangoDB.Cursor
- Executes a query. For example:query(aql`...`)
. - aql
(strings: TemplateStringsArray, ...args: any[]) => ArangoDB.Query
- Builds AQL string. - requestedAttributes
string[]
- List of requested attributes. - hasAuth
boolean
- Whether an authorization is required for the current route. - auth
(doc: DocumentData, method?: RouteMethod, action?: RouteAction) => false | DocumentData
- Function to determine access to the document when working with Route.auth. - json
(omitUnwritableAttributes: boolean = true) => DocumentData
- Returns request json without inaccessible attributes. - send
(data: DocumentData, omitUnreadableAttributes: boolean = true) => Foxx.Response
- Strips inaccessible attributes based on roles and sends a response. - error
(status: ArangoDB.HttpStatus, reason?: string) => Foxx.Response
- Function to response with an error. - tags
string[]
- Tags used for the route (collection.name). - summary
string
- Summary of the route - description
string
- Description of the route - deprecated
boolean
- Whether the route has been deprecated
Routes can be further configured by using the following options.
- relations?
string[]
- list of relations that can be fetched using the query paramrelations=entity1,entity2
. 📘 About relations - body?
RouteBody
- pathParams?
[string, Schema, string?][]
- queryParams?
[string, Schema, string?][]
- response?
RouteResponse
- description?
string
- mime
string[]
- schema
Foxx.Schema | Foxx.Model
- status
RouteStatus
- description?
- errors?
[RouteStatus, string][]
- path?
string
- process?
boolean
- handlerName?
string
- handler?
(arg: RouteArg) => any
- Handler of the current request. - roles?
string[]
- List of required roles for accessing the current request.
Creates a GET
route on collectionName/{_key}
.
When used as a ClassDecorator
a default route for returning a single document of the collection is created. When used on a static method of the collection a custom route executing the very same function with the RouteArg argument will be created.
- path?
string
- Rich path string (can contain simple types, i.e./:var=number
). - schema?
(enjoi: Enjoi) => Joi
- Joi schema for accessing the request. - roles?
string[]
- Roles required to access the route. - summary?
string | RouteOpt
- Shortcut foroptions.summary
. - options?
RouteOpt
- See RouteOpt.
The order of the arguments does not matter as long as the options are the last argument.
Example
@Collection(of => User)
// executed as a ClassDecorator - creates a route on `GET users/:_key` to return a User
@Route.GET(roles => ['admin'])
class Users extends Entities {
// executed as a ProperyDecorator - creates a route on `GET users/hello-world?password`
@Route.GET(
path => 'hello/world?password',
roles => ['user'],
$ => ({
password: $(String).min(6)
})
)
static GET({ json, error, collection }: RouteArg){
const { password } = json();
if(password !== 'top-secret')
return error('forbidden');
return collection._document('123');
}
}
Creates a POST
route on collectionName/{_key}
. Provides a route to create documents by using collection._insert
, when called as a ClassDecorator
.
- path?
string
- Rich path string (can contain simple types, i.e./:var=number
). - schema?
(enjoi: Enjoi) => Joi
- Joi schema for accessing the request. - roles?
string[]
- Roles required to access the route. - summary?
string | RouteOpt
- Shortcut foroptions.summary
. - options?
RouteOpt
- See RouteOpt.
The order of the arguments does not matter as long as the options are the last argument.
Example
@Collection(of => User)
// executed as a ClassDecorator - creates a route on `POST users/:_key` to create Users
@Route.POST(roles => ['guest'])
class Users extends Entities {
// executed as a ProperyDecorator - creates a route on `POST users/hello-world?password`
@Route.POST('hello-world')
static POST({ aql, query }: RouteArg){
return query(aql`
FOR u IN Users
FILTER u._key == '123'
RETURN u
`).toArray():
}
}
Creates a PATCH
route on collectionName/{_key}
. Provides a route to update single documents by using collection._update
, when called as a ClassDecorator
.
- path?
string
- Rich path string (can contain simple types, i.e./:var=number
). - schema?
(enjoi: Enjoi) => Joi
- Joi schema for accessing the request. - roles?
string[]
- Roles required to access the route. - summary?
string | RouteOpt
- Shortcut foroptions.summary
. - options?
RouteOpt
- See RouteOpt.
The order of the arguments does not matter as long as the options are the last argument.
Example
@Collection(of => User)
// executed as a ClassDecorator - creates a route on `PATCH users/:_key` to update Users
@Route.PATCH(roles => ['guest'])
class Users extends Entities {
// executed as a ProperyDecorator - creates a route on `PATCH users/foo`
@Route.PATCH('foo', ['admin'])
static PATCH({ res }: RouteArg){
res.send('foo');
}
}
The same as Route.PATCH
but instead of using collection._update
it uses collection._replace
.
Example
@Collection(of => User)
// executed as a ClassDecorator - creates a route on `PUT users/:_key` to replace Users
@Route.PUT(roles => ['guest'])
class Users extends Entities {
// executed as a ProperyDecorator - creates a route on `PUT users/bar`
@Route.PUT('bar', ['admin'])
static PUT({ res }: RouteArg){
res.send('bar');
}
}
Creates a DELETE
route on collectionName/{_key}
. Provides a route to remove single documents by using collection._remove
, when called as a ClassDecorator
.
- path?
string
- Rich path string (can contain simple types, i.e./:var=number
). - schema?
(enjoi: Enjoi) => Joi
- Joi schema for accessing the request. - roles?
string[]
- Roles required to access the route. - summary?
string | RouteOpt
- Shortcut foroptions.summary
. - options?
RouteOpt
- See RouteOpt.
Example
@Collection(of => User)
// executed as a ClassDecorator - creates a route on `DELETE users/:_key` to create Users
@Route.DELETE(roles => ['guest'])
class Users extends Entities {
// executed as a ProperyDecorator - creates a route on `DELETE users/baz`
@Route.DELETE('baz', ['admin'])
static DELETE({ res }: RouteArg){
res.send('baz');
}
}
Creates a GET
route on /collectionName
for returning a list of documents. Any provided queryParams
will be used to filter the list. Additionally the parameters limit, offset, sort & order can be used. For more information see swagger docs.
- schema?
(enjoi: Enjoi) => Joi
- Joi schema for accessing the requestsqueryParams
Any values will be used to filter the result list. Use required attributes to avoid full collection access. - roles?
string[]
- Roles required to access the route. - summary?
string | RouteOpt
- Shortcut foroptions.summary
. - options?
RouteOpt
- See RouteOpt.
Example
@Collection(of => User)
// creates a route on `GET users?country=US`
// to return User[] with User.country == 'US'
@Route.LIST($ => ({country:['US','DE']}), roles => ['guest'])
class Users extends Entities {}
Extends AQL with a User Function. The function name is derived from the collection name and the method name. For example a USERS::METHOD_NAME()
. The parameter order does not matter.
- isDeterministic?
() => boolean
- Specify whether the function results are fully deterministic (i.e. depend solely on the input and are the same for repeated calls with the same input values). - customName?
() => string
- By default the name of the function is concatenated from the collection- and method name seprated by::
. A different name can be provided, however the collection name will always be the prefix.
Example
@Collection(of => User)
class Users extends Entities {
// registers "USERS::FUNCTION_NAME()"
@AQLFunction(isDeterministic => true)
static FUNCTION_NAME(arg){
return true;
}
// registers"USERS::LAZYPI()"
@AQLFunction(name => 'LAZIPI', isDeterministic => true)
static IGNORED(arg){
return 3.14;
}
}
Creates a task with the ArangoDB Task Management. The task is either invoked once after startup (when using period => 0
) or every n
seconds. By default the task id equals to CollectioName/MethodName
.
⚠️ It is important to note that the callback function is late bound and will be executed in a different context than in the creation context. The callback function must therefore not access any variables defined outside of its own scope. The callback function can still define and use its own variables. Read more
- period?
() => number | Options
- Number of seconds to wait in between executions. Note: when set to zero the function will be executed only once. This argument can also be an options object containing itself and the other arguments below (eg{period:2,name:'abc'}
). - params?
{[key:string}: any]
- To pass parameters to a task. Note that the parameters are limited to data types usable in JSON (meaning no callback functions can be passed as parameters into a task). - name?
string
- names are informational only. They can be used to make a task distinguishable from other tasks also running on the server.
Example
@Collection(of => User)
class Users extends Entities {
// executes the method every 10 seconds (task-id = "Users/MY_TASK")
// Note: the below is equal to @Task({period:10,name:...,params:...})
@Task(period => 10, name => 'Log something', {really:true})
static MY_TASK(params){
console.log('Hello World',params);
}
}
Provides simple internationalization support by storing strings in multiple languages as nested objects. If this type is returned in a request with a locale
parameter or session().data.locale
provided, only the respective value will be returned - or if none provided the english (en
) value.
Set the query parameter locale
to *
in order to return all values from a route.
Don't hesitate to use country specific languages like
de-CH
. When a provided locale has no direct match, the country code is ignored and the global locale value (de
) will be returned.
Example
@Document()
class Page extends Entity {
@Attribute()
title: Type.I18n;
}
@Collection(of => Page)
@Route.GET()
class Pages extends Entities { }
// document in collection
{ "_key": "1", "title": { "en": "Hello World", "de": "Hallo Welt", "de-CH": "Grüezi Welt" } }
// request examples
// GET pages/1?locale=de-AT => {title:'Hallo Welt'}
// GET pages/1 && session:{data:{locale:'de-CH'}} => {title:'Grüezi Welt'}
// GET pages/1 => {title:'Hello World'}
// GET pages/1?locale=* => original value
Provides support for storing numbers in multiple currency objects:
"price": {
"EUR": 1,
"USD": 1.12,
"CAD": 1.45,
...
}
If this type is returned in a request with a currency
parameter or session().data.currency
provided, only the respective value will be returned - or if none provided the value of config.defaultCurrency
(default: USD).
Set the query parameter currency
to *
in order to return all values from a route.
Example
@Document()
class Product extends Entity {
@Attribute()
price: Type.Currencies;
}
@Collection(of => Product)
@Route.GET()
class Products extends Entities { }
// document in collection
{ "_key": "1", "price": { "EUR": 1, "USD": 1.12, "CAD": 1.45 } }
// request examples
// GET products/1?currency=EUR => {price:1}
// GET products/1 && session:{data:{currency:'CAD'}} => {price:1.45}
// GET products/1?currency=* => original value
Sets a value of new Date()
whenever a new document is created.
This is really just another way of using the
@Before.insert(resolver)
decorator
Example
@Document()
class User extends Entity {
@Attribute()
createdAt: Type.DateInsert;
}
@Collection(of => User)
@Route.POST()
class Users extends Entities { }
// request
// POST /users/1 => {_key: "1", createdAt: "2019-03-18T12:00:00.000Z"}
Sets a value of new Date()
whenever a document is updated.
This is really just another way of using the
@Before.insert(resolver)
decorator
Example
@Document()
class User extends Entity {
@Attribute()
updatedAt: Type.DateUpdate;
}
@Collection(of => User)
@Route.PATCH()
class Users extends Entities { }
// request
// PATCH /users/1 => {_key: "1", updatedAt: "2019-03-18T12:00:00.001Z"}
Does not really do anything as of now. Can decorate a Class
, Propery
or Method
and might be to used to further describe routes / entities in the future.
Example
@Document()
@Description('Every user has a single profile document with a relation on User.profile')
class UserProfile extends Entitiy {
@Attribute()
@Description('This attribute can be a useless')
useless: boolean
}
Joi originates from plain JavaScript, but now that we have access to Types, it can be enhanced. Therefore type-arango comes with Enjoi
which is a simple wrapper around Joi
. Enjoi is especially useful when using the @Attribute Decorator and it's always involved when there is a mention of $ => ...
in an example.
Example
const string = $(String) // = Joi.string();
const obj = $({
bool: Boolean // = Joi.boolean()
number: $(Number).integer(), // = Joi.number().integer()
valid: ['valid','strings'], // = Joi.any().valid('valid','strings')
attribute: $(User).email // = Joi.string().email() (from User entity)
}); // = Joi.object().keys(...)
In order to allow clients to provide their own operators (ie. for LIST
or custom routes), the parameter schema of any route can be extended by calling the custom joi method StringSchema.operator(nameOrList?)
.
Clients can then provide the operator as a prefix of the parameter value - separated by config.paramOperatorSeparator
(default |
). For example ?attr=>=|10
.
==, !=, <, <=, >, >=, IN, NOT IN, LIKE, =
, !
These parameters will be parsed by TypeArango in order to validate them and to transform the value into a tuple of [OPERATOR, VALUE]
- it can then use it within its internal queryBuilder (for LIST
requests).
They will also be documented in ArangoDBs Web Interface Swagger Docs.
@Collection(of => User)
@Route.LIST('operator', $ => ({
param1: $(String), // allow == (default)
param2: $(String).operator('!=') // allow !=
param3: $(String).operator(['LIKE','NOT LIKE']), // u get the point
param4: $(String).operator() // allow all: ==, !=, <, <=, >, >=, IN, NOT IN, LIKE
}))
class Users {
@Route.GET('custom', $ => ({
test: $(SomeEntity).someAttribute.operator(['!=', 'LIKE'])
}))
static GET_CUSTOM({param}): RouteArg {
return param.test;
}
}
GET users/custom?test=LIKE|Searc%
Response 200: [ 'LIKE', 'Searc%']
GET users/custom?test=A
Response 200: [ '==', 'A']
GET users/custom?test=!!|Invalid
Response 200: [ '==', '!!|Invalid'] (invalid operators are ignored)
GET users/custom?test=>=|Nope
Response 400: query parameter "test" operator ">=" must be one of [!=, LIKE]
Note: The operator
LIKE
is case-sensitive. When capitalization should be ignored the regexp operator=~
can be combined with a flag (?param==~|(?i)search
) to match case-insensitive.
The decorator @Route.groups
expects CRUD roles but provides five instead of the expected four routes, this is intended because the updateRoles
can either PATCH
(update) or PUT
(replace) an entity.
Method | CRUD | Roles |
---|---|---|
GET | Read | readers |
LIST | Read | readers |
POST | Create | creators |
PATCH | Update | updaters |
PUT | Update | updaters |
DELETE | Delete | deleters |
Note: Fields have only two access roles:
readers
andwriters
.