#+TITLE: @apparts/model-api
#+DATE: [2021-02-08 Mon]
#+AUTHOR: Philipp Uhl
Generate CRUDish API-endpoints for a model defined with the
[[https://github.com/apparts-js/apparts-model][@apparts/model]] package.
#+BEGIN_SRC js
npm i --save @apparts/model-api
#+END_SRC
To generate CRUD-like API-endpoints for a model, use the =addCrud=
function.
- =addCrud({ prefix, app, model, routes })=
- =prefix : string= :: The prefix for the URI, can contain
parameters (see next section).
- =app : App= :: The express app
- =model : function= :: A function that returns an array in
the form of =[OneModel, ManyModel, NoneModel]= (as does the
=makeModel= function from [[https://github.com/apparts-js/apparts-model][@apparts/model]] package).
- =routes : object= :: An object that contains options for the routes
to be generated. If one of the keys is not present in the object,
that route will not be generated. Possible keys are:
- =get= :: The get all route
- =getByIds= :: The get by ids route
- =post= :: The create item route
- =put= :: The update item route
- =patch= :: The patch item route
- =delete= :: The delete items route
The values for each of these keys are objects, that can contain:
- =hasAccess : (request) -> Promise= :: A functions that defines
access rights for the individual endpoint. It receives the same
parameters as a request handler from [[https://github.com/apparts-js/apparts-types][@apparts/types]] receives. If
the function throws an HttpError (from [[https://github.com/apparts-js/apparts-prep][@apparts/prep]]), access
will be rejected. If another error is thrown, a 500 error will
be produced. Otherwise access will be granted.
- =title : string= :: A custom title for the documentation
- =description : string= :: A custom description for the
documentation
- injectParameters : { [key of model]: (request) => Promise<typeof model[key]> } ::
An object that takes keys of the models' data for keys and
functions that return a value for said models' key. This can be
usefull if a certain parameter should be defined based on the
request. Every key that is injected will not be available in the
body or query parameters of the endpoints.
- =idField?: string= :: (optional, default value is =id=) When using a
field for identifying, named different than =id=, you must specify
the fields name using this parameter. This also changes the routes
to use the =idField='s value instead of =id= and =(idField + 's')= for
the pluralizations of =id= (e.g. =GET /v/1/user/:customIds= for
=idField= of =customId=).
- extraPathFields? :: (optional) A schema (as defined by
[[https://github.com/apparts-js/apparts-types][@apparts/types]]) that specifies additional path params that should
be ignored by the endpoints.
#+BEGIN_SRC js
const { addCrud, anybody } = require("@apparts/model-api");
const addRoutes = (app) => {
// ...add your routes
addCrud("/v/1/user", app, useUser, {
get: anybody, getByIds: anybody, post: anybody, put: anybody, patch: anybody, delete: anybody,
});
};
#+END_SRC
Adds these routes:
- =GET /v1/user= :: The get all route (=get=)
- =GET /v/1/user/:ids= :: The get by ids route (=getByIds=)
- =POST /v/1/user= :: The create item route (=post=)
- =PUT /v/1/user/:id= :: The update item route (=put=)
- =PATCH /v/1/user/:id= :: The patch item route (=patch=)
- =DELETE /v/1/user/:ids= :: The delete items route (=delete=)
All these routes have access control defined by you through the =access=
parameter when defining the routes.
** Custom route prefixes for more REST-style access
#+BEGIN_SRC js
const { addCrud, anybody } = require("@apparts/model");
// create comment model with this type:
const types = {
id: {
type: "id",
public: true,
auto: true,
key: true
},
userid: { type: "id", public: true },
createdOn: {
type: "time",
default: (c) => c.optionalVal || Date.now()
},
comment: { type: "string", public: true },
};
// add routes
addCrud("/v/1/user/:userid/comment", app, useComments, {
get: { hasAccess: anybody },
getByIds: { hasAccess: anybody },
post: { hasAccess: anybody },
put: { hasAccess: anybody },
patch: { hasAccess: anybody },
delete: { hasAccess: anybody },
});
#+END_SRC
Adds these routes:
- =GET /v/1/user/:userid/comment=
- =GET /v/1/user/:userid/comment/:ids=
- =POST /v/1/user/:userid/comment=
- =PUT /v/1/user/:userid/comment/:id=
- =PATCH /v/1/user/:userid/comment/:id=
- =DELETE /v/1/user/:userid/comment/:ids=
Note, that the parameter =userid= from the route is /automatically/
/matched/ against the =userid= field from the model.
** Access management
In the previous examples, all routes where created accessible for
anybody. That is most likely not what you want. Instead, you can
define a function for each crud operation that decides if access
should be granted. This function receives all parameters of the
API-call and uses them to determine if access should be granted.
To learn more about the access function and how to use it, visit [[https://github.com/apparts-js/apparts-prep#access-control][@apparts/prep - Access Control]].
** Special parameters in the model
When defining the type of your model, you can use all the parameters
as defined by [[https://github.com/phuhl/apparts-model][@apparts/model]] (e.g. =public=, =mapped=, =optional=, =derived=,
=auto=). The generated API endpoints respect these values:
- Only types with =public: true= are shown on GET and can be set with
POST and PUT
- Types with =mapped: true= are shown to the outside with their mapped names
- Types with =optional: true= are optional and don't have to be set
- Types with =auto= or a =derived= function can not be set on PUT or POST
- The =derived= function can be used to fetch sub object as the =derived=
function is called asynchronously.
Additionally, @apparts/model-api respects the value =readOnly=:
- Types with =readOnly: true= can only be read. It's value have to be
created with a =default= function. This can be useful, e.g. for a
created date, that should be readable (i.e. public) but not be
modifiable.